diff --git a/test_data/ontologies/freetest-onto.ttl b/test_data/ontologies/freetest-onto.ttl index c270700310..b252a4a854 100644 --- a/test_data/ontologies/freetest-onto.ttl +++ b/test_data/ontologies/freetest-onto.ttl @@ -1,181 +1,270 @@ -@prefix xml: . -@prefix xsd: . -@prefix rdf: . -@prefix rdfs: . -@prefix owl: . -@prefix foaf: . +@prefix xml: . +@prefix xsd: . +@prefix rdf: . +@prefix rdfs: . +@prefix owl: . +@prefix foaf: . @prefix knora-base: . @prefix salsah-gui: . @base . # A trivial ontology, used only for testing Knora. -@prefix : . - rdf:type owl:Ontology ; - rdfs:label "The free test ontology" ; - knora-base:attachedToProject ; - knora-base:lastModificationDate "2012-12-12T12:12:12.12Z"^^xsd:dateTime . - - - - - -:hasText rdf:type owl:ObjectProperty ; - - rdfs:subPropertyOf knora-base:hasValue ; - - rdfs:label "Text"@de , - "Texte"@fr , - "Testo"@it , - "Text"@en ; - - knora-base:subjectClassConstraint :FreeTest ; - - knora-base:objectClassConstraint knora-base:TextValue ; - - salsah-gui:guiElement salsah-gui:SimpleText ; - - salsah-gui:guiAttribute "size=80" , - "maxlength=255" . - - -:hasInteger rdf:type owl:ObjectProperty ; - - rdfs:subPropertyOf knora-base:hasValue ; - - rdfs:label "Ganzzahl"@de , - "Nombre entier"@fr , - "Intero"@it , - "Integer"@en ; - - knora-base:subjectClassConstraint :FreeTest ; - - knora-base:objectClassConstraint knora-base:IntValue ; - - salsah-gui:guiElement salsah-gui:Spinbox ; - - salsah-gui:guiAttribute "min=0" , - "max=-1" . - - -:hasIntegerProperty rdf:type owl:ObjectProperty ; - - rdfs:subPropertyOf knora-base:hasValue ; - - rdfs:label "Ganzzahl"@de , - "Nombre entier"@fr , - "Intero"@it , - "Integer"@en ; +@prefix : . - knora-base:objectClassConstraint knora-base:IntValue ; - - salsah-gui:guiElement salsah-gui:Spinbox ; - - salsah-gui:guiAttribute "min=0" , - "max=-1" . - - -:hasDecimal rdf:type owl:ObjectProperty ; - - rdfs:subPropertyOf knora-base:hasValue ; - - rdfs:label "Dezimalzahl"@de , - "Nombre décimal"@fr , - "Numero decimale"@it , - "Decimal number"@en ; - - knora-base:subjectClassConstraint :FreeTest ; - - knora-base:objectClassConstraint knora-base:DecimalValue ; - - salsah-gui:guiElement salsah-gui:SimpleText ; - - salsah-gui:guiAttribute "size=80" , - "maxlength=255" . - - - -:hasBoolean rdf:type owl:ObjectProperty ; - - rdfs:subPropertyOf knora-base:hasValue ; - - rdfs:label "Boolescher Wert"@de , - "Valeur booléenne"@fr , - "Valore booleano"@it , - "Boolean value"@en ; - - knora-base:subjectClassConstraint :FreeTest ; - - knora-base:objectClassConstraint knora-base:BooleanValue ; - - salsah-gui:guiElement salsah-gui:Checkbox . - - -:FreeTest rdf:type owl:Class ; - - rdfs:subClassOf knora-base:Resource , - [ - rdf:type owl:Restriction ; - owl:onProperty :hasText ; - owl:minCardinality "1"^^xsd:nonNegativeInteger ; - salsah-gui:guiOrder "1"^^xsd:nonNegativeInteger - ] , - [ - rdf:type owl:Restriction ; - owl:onProperty :hasBoolean ; - owl:maxCardinality "1"^^xsd:nonNegativeInteger ; - salsah-gui:guiOrder "2"^^xsd:nonNegativeInteger - ] , - [ - rdf:type owl:Restriction ; - owl:onProperty :hasDecimal ; - owl:minCardinality "0"^^xsd:nonNegativeInteger ; - salsah-gui:guiOrder "3"^^xsd:nonNegativeInteger - ] , - [ - rdf:type owl:Restriction ; - owl:onProperty :hasInteger ; - owl:minCardinality "0"^^xsd:nonNegativeInteger ; - salsah-gui:guiOrder "4"^^xsd:nonNegativeInteger - ] , - [ - rdf:type owl:Restriction ; - owl:onProperty :hasIntegerProperty ; - owl:minCardinality "0"^^xsd:nonNegativeInteger ; - salsah-gui:guiOrder "5"^^xsd:nonNegativeInteger - ] ; - - knora-base:resourceIcon "thing.png" ; - - rdfs:label "FT de"@de , - "FT fr"@fr , - "FT it"@it , - "FT en"@en ; + + rdf:type owl:Ontology ; + rdfs:label "The free test ontology" ; + knora-base:attachedToProject ; + knora-base:lastModificationDate "2012-12-12T12:12:12.12Z"^^xsd:dateTime . - rdfs:comment """A comment for FT."""@de . -:ShortFreeTest rdf:type owl:Class ; +:hasText + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasValue ; + rdfs:label "Text"@de, "Texte"@fr, "Testo"@it, "Text"@en ; + knora-base:subjectClassConstraint :FreeTest ; + knora-base:objectClassConstraint knora-base:TextValue ; + salsah-gui:guiElement salsah-gui:SimpleText ; + salsah-gui:guiAttribute "size=80", "maxlength=255" . + + +:hasInteger + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasValue ; + rdfs:label "Ganzzahl"@de, "Nombre entier"@fr, "Intero"@it, "Integer"@en ; + knora-base:subjectClassConstraint :FreeTest ; + knora-base:objectClassConstraint knora-base:IntValue ; + salsah-gui:guiElement salsah-gui:Spinbox ; + salsah-gui:guiAttribute "min=0", "max=-1" . + + +:hasIntegerProperty + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasValue ; + rdfs:label "Ganzzahl"@de, "Nombre entier"@fr, "Intero"@it, "Integer"@en ; + knora-base:objectClassConstraint knora-base:IntValue ; + salsah-gui:guiElement salsah-gui:Spinbox ; + salsah-gui:guiAttribute "min=0", "max=-1" . + + +:hasDecimal + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasValue ; + rdfs:label "Dezimalzahl"@de, "Nombre décimal"@fr, "Numero decimale"@it, "Decimal number"@en ; + knora-base:subjectClassConstraint :FreeTest ; + knora-base:objectClassConstraint knora-base:DecimalValue ; + salsah-gui:guiElement salsah-gui:SimpleText ; + salsah-gui:guiAttribute "size=80", "maxlength=255" . + + + +:hasBoolean + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasValue ; + rdfs:label "Boolescher Wert"@de, "Valeur booléenne"@fr, "Valore booleano"@it, + "Boolean value"@en ; + knora-base:subjectClassConstraint :FreeTest ; + knora-base:objectClassConstraint knora-base:BooleanValue ; + salsah-gui:guiElement salsah-gui:Checkbox . + + + +:FreeTest + rdf:type owl:Class ; + rdfs:subClassOf knora-base:Resource, + [ rdf:type + owl:Restriction ; + owl:onProperty :hasText ; + owl:minCardinality + "1"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "1"^^xsd:nonNegativeInteger ], + [ rdf:type + owl:Restriction ; + owl:onProperty + :hasBoolean ; + owl:maxCardinality + "1"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "2"^^xsd:nonNegativeInteger ], + [ rdf:type + owl:Restriction ; + owl:onProperty + :hasDecimal ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "3"^^xsd:nonNegativeInteger ], + [ rdf:type + owl:Restriction ; + owl:onProperty + :hasInteger ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "4"^^xsd:nonNegativeInteger ], + [ rdf:type + owl:Restriction ; + owl:onProperty + :hasIntegerProperty ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "5"^^xsd:nonNegativeInteger ] ; + knora-base:resourceIcon "thing.png" ; + rdfs:label "FT de"@de, "FT fr"@fr, + "FT it"@it, "FT en"@en ; + rdfs:comment """A comment for FT."""@de . + + +:ShortFreeTest + rdf:type owl:Class ; rdfs:subClassOf :FreeTest ; - rdfs:label "SFT de"@de , - "SFT fr"@fr , - "SFT it"@it , - "SFT en"@en ; - - rdfs:comment """A comment for SFT."""@de . - - -:FreeTestResourceClass rdf:type owl:Class ; - rdfs:subClassOf knora-base:Resource , - [ - rdf:type owl:Restriction ; - owl:onProperty :hasIntegerProperty ; - owl:minCardinality "0"^^xsd:nonNegativeInteger ; - salsah-gui:guiOrder "1"^^xsd:nonNegativeInteger - ] ; - rdfs:label "FTRC de"@de , - "FTRC fr"@fr , - "FTRC it"@it , - "FTRC en"@en ; - - rdfs:comment """A comment for FTRC."""@de . + rdfs:label "SFT de"@de, "SFT fr"@fr, "SFT it"@it, "SFT en"@en ; + rdfs:comment """A comment for SFT."""@de . + + +:FreeTestResourceClass + rdf:type owl:Class ; + rdfs:subClassOf knora-base:Resource, [ rdf:type owl:Restriction ; + owl:onProperty :hasIntegerProperty ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder "1"^^xsd:nonNegativeInteger ] ; + rdfs:label "FTRC de"@de, "FTRC fr"@fr, "FTRC it"@it, "FTRC en"@en ; + rdfs:comment """A comment for FTRC."""@de . + + +:Author + rdf:type owl:Class ; + rdfs:subClassOf knora-base:Resource, [ rdf:type owl:Restriction ; + owl:onProperty :hasName ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder "1"^^xsd:nonNegativeInteger ] ; + rdfs:label "Autor"@de, "Author"@en ; + rdfs:comment """An author"""@en . + + +:ScientificAuthor + rdf:type owl:Class ; + rdfs:subClassOf :Author ; + rdfs:label "Wissenschaftlicher Autor"@de, "Scientific author"@en ; + rdfs:comment """A scientific author"""@en . + + +:hasName + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasValue ; + rdfs:label "Name"@de, "Nom"@fr, "Nome"@it, "Name"@en ; + knora-base:objectClassConstraint knora-base:TextValue ; + salsah-gui:guiElement salsah-gui:SimpleText ; + salsah-gui:guiAttribute "size=80", "maxlength=255" . + + +:hasAuthor + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasLinkTo ; + rdfs:label "Autor"@de, "Auteur"@fr, "Autore"@it, "Author"@en ; + knora-base:subjectClassConstraint :Book ; + knora-base:objectClassConstraint :Author ; + salsah-gui:guiElement salsah-gui:Searchbox . + +:hasAuthorValue + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasLinkToValue ; + rdfs:label "Autor"@de, "Auteur"@fr, "Autore"@it, "Author"@en ; + knora-base:subjectClassConstraint :Book ; + knora-base:objectClassConstraint knora-base:LinkValue ; + salsah-gui:guiElement salsah-gui:Searchbox . + +:hasScientificAuthor + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf :hasAuthor ; + rdfs:label "Wissenschaftlicher Autor"@de, "Scientific author"@en ; + knora-base:subjectClassConstraint :ScientificBook ; + knora-base:objectClassConstraint :ScientificAuthor ; + salsah-gui:guiElement salsah-gui:Searchbox . + +:hasScientificAuthorValue + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf :hasAuthorValue ; + rdfs:label "Wissenschaftlicher Autor"@de, "Scientific author"@en ; + knora-base:subjectClassConstraint :ScientificBook ; + knora-base:objectClassConstraint knora-base:LinkValue ; + salsah-gui:guiElement salsah-gui:Searchbox . + +:Book + rdf:type owl:Class ; + rdfs:subClassOf knora-base:Resource ; + rdfs:subClassOf [ rdf:type owl:Restriction ; + owl:onProperty :hasAuthor ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; + owl:onProperty :hasAuthorValue ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "2"^^xsd:nonNegativeInteger ] ; + rdfs:label "Buch"@de, "Book"@en ; + rdfs:comment """A comment for book"""@en . + +:ScientificBook + rdf:type owl:Class ; + rdfs:subClassOf :Book ; + rdfs:subClassOf [ rdf:type owl:Restriction ; + owl:onProperty :hasScientificAuthor ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; + owl:onProperty :hasScientificAuthorValue ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "2"^^xsd:nonNegativeInteger ] ; + rdfs:label "Wissenschaftliches Buch"@de, + "Scientific book"@en ; + rdfs:comment """A comment for a scientific book"""@en . + +:hasOtherCULP + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasLinkTo ; + rdfs:label "Ein anderes CULP"@de, "Une autre CULP"@fr, "Un'altra CULP"@it, + "Another CULP"@en ; + knora-base:subjectClassConstraint :ClassUsingLinkProperties ; + knora-base:objectClassConstraint :ClassUsingLinkProperties ; + salsah-gui:guiElement salsah-gui:Searchbox . + + +:hasOtherCULPValue + rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf knora-base:hasLinkToValue ; + rdfs:label "Ein anderes Ding"@de, "Une autre chose"@fr, "Un'altra cosa"@it, + "Another thing"@en ; + knora-base:subjectClassConstraint :ClassUsingLinkProperties ; + knora-base:objectClassConstraint knora-base:LinkValue ; + salsah-gui:guiElement salsah-gui:Searchbox . + +:ClassUsingLinkProperties + rdf:type owl:Class ; + rdfs:subClassOf knora-base:Resource ; + rdfs:subClassOf [ rdf:type owl:Restriction ; + owl:onProperty :hasOtherCULP ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "3"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; + owl:onProperty :hasOtherCULPValue ; + owl:minCardinality + "0"^^xsd:nonNegativeInteger ; + salsah-gui:guiOrder + "4"^^xsd:nonNegativeInteger ] ; + rdfs:label "CULP de"@de, "CULP fr"@fr, "CULP it"@it, + "CULP en"@en ; + rdfs:comment """A comment for CULP."""@de . diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala index 2a85f758ed..8b00896121 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala @@ -2644,11 +2644,26 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ontology.properties ++ maybeLinkValuePropertyCacheEntry + (internalPropertyIri -> readPropertyInfo) ) + // if a link value property was created, add its subproperty relation to the ontology's subPropertyOfRelations map + // note: this is only needed for the special case of link properties that are subproperties of custom link properties + + maybeSubPropertyOfRelationsForLinkValueProperty: Option[(SmartIri, Set[SmartIri])] = + maybeLinkValuePropertyCacheEntry.map { case (smartIri: SmartIri, readPropertyInfoV2: ReadPropertyInfoV2) => + smartIri -> readPropertyInfoV2.entityInfoContent.subPropertyOf + } + + newSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]] = + maybeSubPropertyOfRelationsForLinkValueProperty match { + case Some(smartIriSetOfSmartIris: (SmartIri, Set[SmartIri])) => + Map(internalPropertyIri -> allKnoraSuperPropertyIris, smartIriSetOfSmartIris) + case None => + Map(internalPropertyIri -> allKnoraSuperPropertyIris) + } + _ = Cache.storeCacheData( cacheData.copy( ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subPropertyOfRelations = - cacheData.subPropertyOfRelations + (internalPropertyIri -> allKnoraSuperPropertyIris) + subPropertyOfRelations = cacheData.subPropertyOfRelations ++ newSubPropertyOfRelations ) ) diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala index 4528e4343d..6aea291f4f 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala @@ -1955,7 +1955,13 @@ object OntologyHelpers { OntologyConstants.KnoraBase.IsPartOfValue.toSmartIri case subProps if subProps.contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri) => OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri - case _ => OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri + case subProps + if subProps.size == 1 => // if subPropertyOf is neither isPartOf nor HasLinkTo it inherits from a custom link property + internalPropertyDef.subPropertyOf.head.fromLinkPropToLinkValueProp + case _ => + throw BadRequestException( + s"Can't create link value property $linkValuePropIri. Probably because it has several super properties." + ) } internalPropertyDef.copy( diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala index 3f0bc05fed..a78f8d0967 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala @@ -62,12 +62,13 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { private val ExampleSharedOntologyIri = "http://api.knora.org/ontology/shared/example-box/v2".toSmartIri private val IncunabulaOntologyIri = "http://0.0.0.0:3333/ontology/0803/incunabula/v2".toSmartIri private val AnythingOntologyIri = "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri + private val FreeTestOntologyIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2".toSmartIri private val printErrorMessages = false private var fooLastModDate: Instant = Instant.now private var barLastModDate: Instant = Instant.now private var chairLastModDate: Instant = Instant.now private var anythingLastModDate: Instant = Instant.parse("2017-12-19T15:23:42.166Z") - private var freetestLastModData: Instant = Instant.parse("2012-12-12T12:12:12.12Z") + private var freetestLastModDate: Instant = Instant.parse("2012-12-12T12:12:12.12Z") val anythingOntology = "http://0.0.0.0:3333/ontology/0001/anything/v2#" val anythingThing: IRI = anythingOntology + "Thing" @@ -945,6 +946,226 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { } + "create a subproperty of an existing custom link property and add it to a resource class, check if the correct link and link value properties were added to the class" in { + + responderManager ! OntologyMetadataGetByProjectRequestV2( + projectIris = Set(anythingProjectIri), + requestingUser = anythingAdminUser + ) + + val metadataResponse = expectMsgType[ReadOntologyMetadataV2](timeout) + assert(metadataResponse.ontologies.size == 3) + freetestLastModDate = metadataResponse + .toOntologySchema(ApiV2Complex) + .ontologies + .find(_.ontologyIri == FreeTestOntologyIri) + .get + .lastModificationDate + .get + + // Create class freetest:ComicBook which is a subclass of freetest:Book + + val comicBookClassIri = FreeTestOntologyIri.makeEntityIri("ComicBook") + + val comicBookClassInfoContent = ClassInfoContentV2( + classIri = comicBookClassIri, + predicates = Map( + OntologyConstants.Rdf.Type.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdf.Type.toSmartIri, + objects = Seq(SmartIriLiteralV2(OntologyConstants.Owl.Class.toSmartIri)) + ), + OntologyConstants.Rdfs.Label.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdfs.Label.toSmartIri, + objects = Seq(StringLiteralV2("Comic Book", Some("en"))) + ), + OntologyConstants.Rdfs.Comment.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdfs.Comment.toSmartIri, + objects = Seq(StringLiteralV2("A comic book", Some("en"))) + ) + ), + directCardinalities = Map(), + subClassOf = Set(FreeTestOntologyIri.makeEntityIri("Book")), + ontologySchema = ApiV2Complex + ) + + responderManager ! CreateClassRequestV2( + classInfoContent = comicBookClassInfoContent, + lastModificationDate = freetestLastModDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + val metadata = externalOntology.ontologyMetadata + val newFreetestLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate + } + + // Create class freetest:ComicAuthor which is a subclass of feetest:Author + + val comicAuthorClassIri = FreeTestOntologyIri.makeEntityIri("ComicAuthor") + + val comicAuthorClassInfoContent = ClassInfoContentV2( + classIri = comicAuthorClassIri, + predicates = Map( + OntologyConstants.Rdf.Type.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdf.Type.toSmartIri, + objects = Seq(SmartIriLiteralV2(OntologyConstants.Owl.Class.toSmartIri)) + ), + OntologyConstants.Rdfs.Label.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdfs.Label.toSmartIri, + objects = Seq(StringLiteralV2("Comic Author", Some("en"))) + ), + OntologyConstants.Rdfs.Comment.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdfs.Comment.toSmartIri, + objects = Seq(StringLiteralV2("A comic author", Some("en"))) + ) + ), + directCardinalities = Map(), + subClassOf = Set(FreeTestOntologyIri.makeEntityIri("Author")), + ontologySchema = ApiV2Complex + ) + + responderManager ! CreateClassRequestV2( + classInfoContent = comicAuthorClassInfoContent, + lastModificationDate = freetestLastModDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + val metadata = externalOntology.ontologyMetadata + val newFreetestLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate + } + + // Create property freetest:hasComicBookAuthor which is a subproperty of freetest:hasAuthor and links freetest:ComicBook and freetest:ComicAuthor + + val comicAuthorPropertyIri = FreeTestOntologyIri.makeEntityIri("hasComicAuthor") + + val comicAuthorPropertyInfoContent = PropertyInfoContentV2( + propertyIri = comicAuthorPropertyIri, + predicates = Map( + OntologyConstants.Rdf.Type.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdf.Type.toSmartIri, + objects = Seq(SmartIriLiteralV2(OntologyConstants.Owl.ObjectProperty.toSmartIri)) + ), + OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri, + objects = Seq(SmartIriLiteralV2(FreeTestOntologyIri.makeEntityIri("ComicBook"))) + ), + OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri, + objects = Seq(SmartIriLiteralV2(FreeTestOntologyIri.makeEntityIri("ComicAuthor"))) + ), + OntologyConstants.Rdfs.Label.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdfs.Label.toSmartIri, + objects = Seq( + StringLiteralV2("Comic author", Some("en")) + ) + ), + OntologyConstants.Rdfs.Comment.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdfs.Comment.toSmartIri, + objects = Seq( + StringLiteralV2("A comic author of a comic book", Some("en")) + ) + ) + ), + subPropertyOf = Set(FreeTestOntologyIri.makeEntityIri("hasAuthor")), + ontologySchema = ApiV2Complex + ) + + responderManager ! CreatePropertyRequestV2( + propertyInfoContent = comicAuthorPropertyInfoContent, + lastModificationDate = freetestLastModDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + val property = externalOntology.properties(comicAuthorPropertyIri) + assert(property.isLinkProp) + assert(!property.isLinkValueProp) + externalOntology.properties(comicAuthorPropertyIri).entityInfoContent should ===( + comicAuthorPropertyInfoContent + ) + val metadata = externalOntology.ontologyMetadata + val newFreetestLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate + } + + // Add new subproperty freetest:hasComicBookAuthor to class freetest:ComicBook + + responderManager ! AddCardinalitiesToClassRequestV2( + classInfoContent = ClassInfoContentV2( + predicates = Map( + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri -> PredicateInfoV2( + predicateIri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri, + objects = Vector(SmartIriLiteralV2(value = "http://www.w3.org/2002/07/owl#Class".toSmartIri)) + ) + ), + classIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2#ComicBook".toSmartIri, + ontologySchema = ApiV2Complex, + directCardinalities = Map( + "http://0.0.0.0:3333/ontology/0001/freetest/v2#hasComicAuthor".toSmartIri -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne + ) + ) + ), + lastModificationDate = freetestLastModDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val comicBookClass = + msg.classes("http://www.knora.org/ontology/0001/freetest#ComicBook".toSmartIri) + val linkProperties = comicBookClass.linkProperties + val linkValueProperties = comicBookClass.linkValueProperties + assert( + linkProperties.contains( + "http://www.knora.org/ontology/0001/freetest#hasComicAuthor".toSmartIri + ) + ) + assert( + linkValueProperties.contains( + "http://www.knora.org/ontology/0001/freetest#hasComicAuthorValue".toSmartIri + ) + ) + assert( + !linkProperties.contains( + "http://www.knora.org/ontology/0001/freetest#hasAuthor".toSmartIri + ) + ) + assert( + !linkValueProperties.contains( + "http://www.knora.org/ontology/0001/freetest#hasAuthorValue".toSmartIri + ) + ) + val newFreetestLastModDate = msg.ontologyMetadata.lastModificationDate + .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate + } + + } + "not create a property without an rdf:type" in { val propertyIri = AnythingOntologyIri.makeEntityIri("wrongProperty") @@ -5758,7 +5979,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ontologySchema = ApiV2Complex, subClassOf = Set("http://api.knora.org/ontology/knora-api/v2#Resource".toSmartIri) ), - lastModificationDate = freetestLastModData, + lastModificationDate = freetestLastModDate, apiRequestID = UUID.randomUUID, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingAdminUser @@ -5767,8 +5988,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgPF(timeout) { case msg: ReadOntologyV2 => val newFreetestLastModDate = msg.ontologyMetadata.lastModificationDate .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newFreetestLastModDate.isAfter(freetestLastModData)) - freetestLastModData = newFreetestLastModDate + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate } // Create a text property. @@ -5814,7 +6035,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { subPropertyOf = Set("http://api.knora.org/ontology/knora-api/v2#hasValue".toSmartIri), ontologySchema = ApiV2Complex ), - lastModificationDate = freetestLastModData, + lastModificationDate = freetestLastModDate, apiRequestID = UUID.randomUUID, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingAdminUser @@ -5823,8 +6044,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgPF(timeout) { case msg: ReadOntologyV2 => val newFreetestLastModDate = msg.ontologyMetadata.lastModificationDate .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newFreetestLastModDate.isAfter(freetestLastModData)) - freetestLastModData = newFreetestLastModDate + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate } // Create an integer property. @@ -5870,7 +6091,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { subPropertyOf = Set("http://api.knora.org/ontology/knora-api/v2#hasValue".toSmartIri), ontologySchema = ApiV2Complex ), - lastModificationDate = freetestLastModData, + lastModificationDate = freetestLastModDate, apiRequestID = UUID.randomUUID, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingAdminUser @@ -5879,8 +6100,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgPF(timeout) { case msg: ReadOntologyV2 => val newFreetestLastModDate = msg.ontologyMetadata.lastModificationDate .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newFreetestLastModDate.isAfter(freetestLastModData)) - freetestLastModData = newFreetestLastModDate + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate } // Add cardinalities to the class. @@ -5904,7 +6125,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) ) ), - lastModificationDate = freetestLastModData, + lastModificationDate = freetestLastModDate, apiRequestID = UUID.randomUUID, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingAdminUser @@ -5913,8 +6134,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgPF(timeout) { case msg: ReadOntologyV2 => val newFreetestLastModDate = msg.ontologyMetadata.lastModificationDate .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newFreetestLastModDate.isAfter(freetestLastModData)) - freetestLastModData = newFreetestLastModDate + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate } // Create a resource of #BlueTestClass using only #hasBlueTestIntProp. @@ -5969,7 +6190,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) ) ), - lastModificationDate = freetestLastModData, + lastModificationDate = freetestLastModDate, apiRequestID = UUID.randomUUID, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingAdminUser @@ -5997,7 +6218,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) ) ), - lastModificationDate = freetestLastModData, + lastModificationDate = freetestLastModDate, apiRequestID = UUID.randomUUID, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingAdminUser @@ -6006,8 +6227,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgPF(timeout) { case msg: ReadOntologyV2 => val newFreetestLastModDate = msg.ontologyMetadata.lastModificationDate .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newFreetestLastModDate.isAfter(freetestLastModData)) - freetestLastModData = newFreetestLastModDate + assert(newFreetestLastModDate.isAfter(freetestLastModDate)) + freetestLastModDate = newFreetestLastModDate } // Check that the correct blank nodes were stored for the cardinalities.