diff --git a/.bazelversion b/.bazelversion index 9f844d434b..ee74734aa2 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -rolling +4.1.0 diff --git a/.gitignore b/.gitignore index ae487b9385..af46d9555b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /.project .ijwb +.vscode **/.idea **/*.iml **/*.ipr @@ -56,3 +57,5 @@ dump.rdb dependencies.txt /client-test-data.zip /db_staging_dump.trig +cleandeps.sh +/.metals diff --git a/.scalafmt.conf b/.scalafmt.conf index f0a9f376d1..e94dd85083 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1 +1,21 @@ +version = "2.7.5" maxColumn = 120 +align.preset = most +align.multiline = false +continuationIndent.defnSite = 2 +assumeStandardLibraryStripMargin = true +docstrings = JavaDoc +lineEndings = preserve +includeCurlyBraceInSelectChains = false +danglingParentheses.preset = true +optIn.annotationNewlines = true +newlines.alwaysBeforeMultilineDef = false + +rewrite.rules = [RedundantBraces] + +rewrite.redundantBraces.generalExpressions = false +rewriteTokens = { + "⇒": "=>" + "→": "->" + "←": "<-" +} diff --git a/WORKSPACE b/WORKSPACE index faf681a695..4b46982914 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -109,7 +109,7 @@ http_archive( # scala_config(scala_version = "2.11.12") load("@io_bazel_rules_scala//:scala_config.bzl", "scala_config") -scala_config(scala_version = "2.13.5") +scala_config(scala_version = "2.13.6") # register default and our custom scala toolchain load("@io_bazel_rules_scala//scala:toolchains.bzl", "scala_register_toolchains") diff --git a/docs/03-apis/api-v2/ontology-information.md b/docs/03-apis/api-v2/ontology-information.md index 651c602e66..62fc3ac2a1 100644 --- a/docs/03-apis/api-v2/ontology-information.md +++ b/docs/03-apis/api-v2/ontology-information.md @@ -1541,15 +1541,13 @@ HTTP PUT to http://host/v2/ontologies/cardinalities "@value" : "ONTOLOGY_LAST_MODIFICATION_DATE" }, "@graph" : [ { - { - "@id" : "CLASS_IRI", - "@type" : "owl:Class", - "rdfs:subClassOf" : { - "@type": "owl:Restriction", - "OWL_CARDINALITY_PREDICATE": "OWL_CARDINALITY_VALUE", - "owl:onProperty": { - "@id" : "PROPERTY_IRI" - } + "@id" : "CLASS_IRI", + "@type" : "owl:Class", + "rdfs:subClassOf" : { + "@type": "owl:Restriction", + "OWL_CARDINALITY_PREDICATE": "OWL_CARDINALITY_VALUE", + "owl:onProperty": { + "@id" : "PROPERTY_IRI" } } } ], @@ -1592,6 +1590,75 @@ The response will look like this: } ``` + +### Delete a single cardinality from a class + +If a class is used in data, it is only allowed to delete a cardinality, if the +property a cardinality refers to, is not used inside the data. Also, the property +isn't allowed to be used inside the data in any subclasses of this class. + +``` +HTTP DELETE to http://host/v2/ontologies/cardinalities +``` + +```jsonld +{ + "@id" : "ONTOLOGY_IRI", + "@type" : "owl:Ontology", + "knora-api:lastModificationDate" : { + "@type" : "xsd:dateTimeStamp", + "@value" : "ONTOLOGY_LAST_MODIFICATION_DATE" + }, + "@graph" : [ { + "@id" : "CLASS_IRI", + "@type" : "owl:Class", + "rdfs:subClassOf" : { + "@type": "owl:Restriction", + "OWL_CARDINALITY_PREDICATE": "OWL_CARDINALITY_VALUE", + "owl:onProperty": { + "@id" : "PROPERTY_IRI" + } + } + } ], + "@context" : { + "knora-api" : "http://api.knora.org/ontology/knora-api/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#" + } +} +``` + +`OWL_CARDINALITY_PREDICATE` and `OWL_CARDINALITY_VALUE` must correspond +to the supported combinations given in +[OWL Cardinalities](../../02-knora-ontologies/knora-base.md#owl-cardinalities). (The placeholder +`OWL_CARDINALITY_VALUE` is shown here in quotes, but it should be an +unquoted integer.) + +When a cardinality on a link property is submitted, an identical cardinality +on the corresponding link value property is automatically added (see +[Links Between Resources](../../02-knora-ontologies/knora-base.md#links-between-resources)). + +A successful response will be a JSON-LD document providing the new class +definition (but not any of the other entities in the ontology). + +To check whether a class's cardinality can be deleted: + +``` +HTTP POST to http://host/v2/ontologies/candeletedinalities +``` + +The response will look like this: + +```jsonld +{ + "knora-api:canDo": false, + "@context": { + "knora-api": "http://api.knora.org/ontology/knora-api/v2#" + } +} +``` + ### Changing the GUI Order of Cardinalities To change the GUI order of one or more cardinalities in a class: diff --git a/test_data/all_data/freetest-data.ttl b/test_data/all_data/freetest-data.ttl new file mode 100644 index 0000000000..8ded38859f --- /dev/null +++ b/test_data/all_data/freetest-data.ttl @@ -0,0 +1,82 @@ +@prefix xml: . +@prefix xsd: . +@prefix rdf: . +@prefix rdfs: . +@prefix owl: . +@prefix foaf: . +@prefix knora-base: . +@prefix knora-admin: . +@prefix salsah-gui: . +@prefix freetest: . + + a freetest:FreeTest ; + knora-base:attachedToUser ; + knora-base:attachedToProject ; + knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember"; + knora-base:creationDate "2019-11-29T10:00:00.673298Z"^^xsd:dateTime; + + freetest:hasText ; + freetest:hasBoolean ; + rdfs:label "a free test instance"; + knora-base:isDeleted false . + + a knora-base:TextValue; + knora-base:valueHasUUID "SZyeLLmOTcCCuS3B0VksHQ"^^xsd:string; + knora-base:isDeleted false; + knora-base:valueCreationDate "2018-05-28T15:52:03.897Z"^^xsd:dateTime; + knora-base:valueHasOrder 0; + knora-base:valueHasString "test"; + knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser"; + knora-base:attachedToUser . + + a knora-base:BooleanValue; + knora-base:valueHasUUID "IN4R19yYR0ygi3K2VEHpUQ"^^xsd:string; + knora-base:isDeleted false; + knora-base:valueCreationDate "2018-05-28T15:52:03.897Z"^^xsd:dateTime; + knora-base:valueHasBoolean true; + knora-base:valueHasOrder 0; + knora-base:valueHasString "true"; + knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser"; + knora-base:attachedToUser . + +## A resource of a subclass + a freetest:ShortFreeTest ; + knora-base:attachedToUser ; + knora-base:attachedToProject ; + knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember"; + knora-base:creationDate "2019-11-29T10:00:00.673298Z"^^xsd:dateTime; + + freetest:hasText ; + freetest:hasBoolean ; + freetest:hasDecimal ; + rdfs:label "a short free test instance"; + knora-base:isDeleted false . + + a knora-base:TextValue; + knora-base:valueHasUUID "SZyeLLmOTcCCuS3B0VksHQ"^^xsd:string; + knora-base:isDeleted false; + knora-base:valueCreationDate "2018-05-28T15:52:03.897Z"^^xsd:dateTime; + knora-base:valueHasOrder 0; + knora-base:valueHasString "test"; + knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser"; + knora-base:attachedToUser . + + a knora-base:BooleanValue; + knora-base:valueHasUUID "IN4R19yYR0ygi3K2VEHpUQ"^^xsd:string; + knora-base:isDeleted false; + knora-base:valueCreationDate "2018-05-28T15:52:03.897Z"^^xsd:dateTime; + knora-base:valueHasBoolean true; + knora-base:valueHasOrder 0; + knora-base:valueHasString "true"; + knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser"; + knora-base:attachedToUser . + + a knora-base:DecimalValue; + knora-base:valueHasUUID "bXMwnrHvQH2DMjOFrGmNzg"^^xsd:string; + knora-base:isDeleted false; + knora-base:valueCreationDate "2018-05-28T15:52:03.897Z"^^xsd:dateTime; + knora-base:valueHasDecimal "1.5"^^xsd:decimal; + knora-base:valueHasOrder 0; + knora-base:valueHasString "1.5"; + knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser"; + knora-base:attachedToUser . diff --git a/test_data/ontologies/freetest-onto.ttl b/test_data/ontologies/freetest-onto.ttl new file mode 100644 index 0000000000..c694bf87c1 --- /dev/null +++ b/test_data/ontologies/freetest-onto.ttl @@ -0,0 +1,143 @@ +@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" . + + + +: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 + ] ; + + 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 . diff --git a/test_data/ontologyR2RV2/allOntologyMetadata.jsonld b/test_data/ontologyR2RV2/allOntologyMetadata.jsonld index 86a9b4337c..dd646280d4 100644 --- a/test_data/ontologyR2RV2/allOntologyMetadata.jsonld +++ b/test_data/ontologyR2RV2/allOntologyMetadata.jsonld @@ -13,6 +13,18 @@ "@type": "owl:Ontology", "@id": "http://0.0.0.0:3333/ontology/0001/anything/v2" }, + { + "knora-api:lastModificationDate": { + "@value": "2012-12-12T12:12:12.120Z", + "@type": "xsd:dateTimeStamp" + }, + "rdfs:label": "The free test ontology", + "knora-api:attachedToProject": { + "@id": "http://rdfh.ch/projects/0001" + }, + "@type": "owl:Ontology", + "@id": "http://0.0.0.0:3333/ontology/0001/freetest/v2" + }, { "knora-api:lastModificationDate": { "@value": "2019-09-10T08:57:46.633162Z", diff --git a/test_data/ontologyR2RV2/allOntologyMetadata.rdf b/test_data/ontologyR2RV2/allOntologyMetadata.rdf index 480c62a2ce..47dd39e019 100644 --- a/test_data/ontologyR2RV2/allOntologyMetadata.rdf +++ b/test_data/ontologyR2RV2/allOntologyMetadata.rdf @@ -1,73 +1,85 @@ - - - - 2017-12-19T15:23:42.166Z - The anything ontology - - - - 2019-09-10T08:57:46.633162Z - A minimal ontology - - - - The something ontology - - - - The images demo ontology - - - - The BEOL ontology - - - - The Biblio ontology - - - - The incunabula ontology - - - - The dokubib ontology - - - - The Anton Webern project ontology - - - - true - The Knora admin ontology - - - - true - The knora-api ontology in the complex schema - - - - true - The salsah-gui ontology - - - - true - 2018-09-10T14:53:00Z - An example of a shared ontology - - - - true - The standoff ontology - - - \ No newline at end of file + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:owl="http://www.w3.org/2002/07/owl#" + xmlns:knora-api="http://api.knora.org/ontology/knora-api/v2#" + xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" + xmlns:xsd="http://www.w3.org/2001/XMLSchema#"> + + true + + The Knora admin ontology + + + The images demo ontology + + + + true + + The salsah-gui ontology + + + The Biblio ontology + + + + + The anything ontology + 2017-12-19T15:23:42.166Z + + + + A minimal ontology + 2019-09-10T08:57:46.633162Z + + + true + + The knora-api ontology in the complex schema + + + true + + The standoff ontology + + + + The free test ontology + 2012-12-12T12:12:12.120Z + + + The Anton Webern project ontology + + + + The BEOL ontology + + + + The dokubib ontology + + + + The incunabula ontology + + + + true + + An example of a shared ontology + 2018-09-10T14:53:00Z + + + The something ontology + + + diff --git a/test_data/ontologyR2RV2/allOntologyMetadata.ttl b/test_data/ontologyR2RV2/allOntologyMetadata.ttl index 9b1ce015cc..2bc82d2e43 100644 --- a/test_data/ontologyR2RV2/allOntologyMetadata.ttl +++ b/test_data/ontologyR2RV2/allOntologyMetadata.ttl @@ -48,6 +48,12 @@ rdfs:label "The images demo ontology" ; knora-api:attachedToProject . + + a owl:Ontology ; + rdfs:label "The knora-api ontology in the complex schema" ; + knora-api:attachedToProject ; + knora-api:isBuiltIn true . + a owl:Ontology ; rdfs:label "The Biblio ontology" ; @@ -58,11 +64,11 @@ rdfs:label "The incunabula ontology" ; knora-api:attachedToProject . - - a owl:Ontology ; - rdfs:label "The knora-api ontology in the complex schema" ; - knora-api:attachedToProject ; - knora-api:isBuiltIn true . + + a owl:Ontology ; + rdfs:label "The free test ontology" ; + knora-api:attachedToProject ; + knora-api:lastModificationDate "2012-12-12T12:12:12.120Z"^^xsd:dateTimeStamp . a owl:Ontology ; diff --git a/test_data/ontologyR2RV2/anythingOntologyMetadata.jsonld b/test_data/ontologyR2RV2/anythingOntologyMetadata.jsonld index 4c6bcecfbe..1dd913191f 100644 --- a/test_data/ontologyR2RV2/anythingOntologyMetadata.jsonld +++ b/test_data/ontologyR2RV2/anythingOntologyMetadata.jsonld @@ -13,6 +13,18 @@ "@type": "owl:Ontology", "@id": "http://0.0.0.0:3333/ontology/0001/anything/v2" }, + { + "knora-api:lastModificationDate": { + "@value": "2012-12-12T12:12:12.120Z", + "@type": "xsd:dateTimeStamp" + }, + "rdfs:label": "The free test ontology", + "knora-api:attachedToProject": { + "@id": "http://rdfh.ch/projects/0001" + }, + "@type": "owl:Ontology", + "@id": "http://0.0.0.0:3333/ontology/0001/freetest/v2" + }, { "knora-api:lastModificationDate": { "@value": "2019-09-10T08:57:46.633162Z", diff --git a/test_data/ontologyR2RV2/anythingOntologyMetadata.rdf b/test_data/ontologyR2RV2/anythingOntologyMetadata.rdf index 57ffe54404..6730a84dfc 100644 --- a/test_data/ontologyR2RV2/anythingOntologyMetadata.rdf +++ b/test_data/ontologyR2RV2/anythingOntologyMetadata.rdf @@ -1,23 +1,29 @@ - - - - 2017-12-19T15:23:42.166Z - The anything ontology - - - - 2019-09-10T08:57:46.633162Z - A minimal ontology - - - - The something ontology - - - \ No newline at end of file + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:owl="http://www.w3.org/2002/07/owl#" + xmlns:knora-api="http://api.knora.org/ontology/knora-api/v2#" + xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" + xmlns:xsd="http://www.w3.org/2001/XMLSchema#"> + + + The anything ontology + 2017-12-19T15:23:42.166Z + + + + A minimal ontology + 2019-09-10T08:57:46.633162Z + + + + The free test ontology + 2012-12-12T12:12:12.120Z + + + The something ontology + + + diff --git a/test_data/ontologyR2RV2/anythingOntologyMetadata.ttl b/test_data/ontologyR2RV2/anythingOntologyMetadata.ttl index d8857a2a9b..153b361080 100644 --- a/test_data/ontologyR2RV2/anythingOntologyMetadata.ttl +++ b/test_data/ontologyR2RV2/anythingOntologyMetadata.ttl @@ -19,3 +19,9 @@ a owl:Ontology ; rdfs:label "The something ontology" ; knora-api:attachedToProject . + + + a owl:Ontology ; + rdfs:label "The free test ontology" ; + knora-api:attachedToProject ; + knora-api:lastModificationDate "2012-12-12T12:12:12.120Z"^^xsd:dateTimeStamp . diff --git a/test_data/ontologyR2RV2/anythingOntologyWithValueObjects.ttl b/test_data/ontologyR2RV2/anythingOntologyWithValueObjects.ttl index a6546ae749..b9b2dff386 100644 --- a/test_data/ontologyR2RV2/anythingOntologyWithValueObjects.ttl +++ b/test_data/ontologyR2RV2/anythingOntologyWithValueObjects.ttl @@ -6,6 +6,15 @@ @prefix knora-api: . @prefix anything: . +anything:hasGeoname a owl:ObjectProperty ; + rdfs:label "Geoname" ; + rdfs:subPropertyOf knora-api:hasValue ; + knora-api:isEditable true ; + knora-api:isResourceProperty true ; + knora-api:objectType knora-api:GeonameValue ; + knora-api:subjectType anything:Thing ; + salsah-gui:guiElement salsah-gui:Geonames . + anything:hasThingPicture a owl:ObjectProperty ; rdfs:label "Picture of a thing" ; @@ -45,31 +54,30 @@ anything:BlueThing a owl:Class ; rdfs:subClassOf anything:Thing ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingPicture + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 2 ; + salsah-gui:guiOrder 7 ; owl:minCardinality 0 ; - owl:onProperty anything:hasText + owl:onProperty anything:hasUri ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 13 ; + salsah-gui:guiOrder 2 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingDocumentValue + owl:onProperty anything:hasText ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -78,100 +86,102 @@ anything:BlueThing a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty anything:hasTimeStamp ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; + salsah-gui:guiOrder 15 ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:onProperty anything:isPartOfOtherThingValue ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 63 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasBlueThing + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 2 ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasRichtext + owl:onProperty anything:hasThingDocumentValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 0 ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasListItem + owl:onProperty anything:hasThingPicture ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - salsah-gui:guiOrder 11 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasGeometry + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 63 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasBlueThingValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + salsah-gui:guiOrder 3 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 4 ; + salsah-gui:guiOrder 0 ; owl:minCardinality 0 ; - owl:onProperty anything:hasInteger + owl:onProperty anything:hasListItem ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 10 ; + salsah-gui:guiOrder 15 ; owl:minCardinality 0 ; - owl:onProperty anything:hasColor + owl:onProperty anything:isPartOfOtherThing ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 6 ; - owl:maxCardinality 1 ; - owl:onProperty anything:hasBoolean + salsah-gui:guiOrder 2 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasRichtext ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 7 ; + salsah-gui:guiOrder 5 ; owl:minCardinality 0 ; - owl:onProperty anything:hasUri + owl:onProperty anything:hasDecimal ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -180,55 +190,43 @@ anything:BlueThing a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 12 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasGeoname - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - salsah-gui:guiOrder 15 ; - owl:minCardinality 0 ; - owl:onProperty anything:isPartOfOtherThingValue + salsah-gui:guiOrder 6 ; + owl:maxCardinality 1 ; + owl:onProperty anything:hasBoolean ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 13 ; + salsah-gui:guiOrder 12 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingDocument + owl:onProperty anything:hasGeoname ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 15 ; + salsah-gui:guiOrder 9 ; owl:minCardinality 0 ; - owl:onProperty anything:isPartOfOtherThing + owl:onProperty anything:hasInterval ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasTimeStamp + owl:onProperty anything:hasThingPictureValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 9 ; + salsah-gui:guiOrder 10 ; owl:minCardinality 0 ; - owl:onProperty anything:hasInterval - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 63 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasBlueThingValue + owl:onProperty anything:hasColor ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 5 ; owl:minCardinality 0 ; - owl:onProperty anything:hasDecimal + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -237,26 +235,37 @@ anything:BlueThing a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + salsah-gui:guiOrder 0 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasOtherListItem ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 0 ; + salsah-gui:guiOrder 11 ; owl:minCardinality 0 ; - owl:onProperty anything:hasOtherListItem + owl:onProperty anything:hasGeometry ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingPictureValue + owl:onProperty anything:hasThingDocument ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 3 ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 63 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasBlueThing + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + salsah-gui:guiOrder 4 ; owl:minCardinality 0 ; - owl:onProperty anything:hasDate + owl:onProperty anything:hasInteger ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true . @@ -280,46 +289,36 @@ anything:ThingPicture rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty anything:hasPictureTitle + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:hasStillImageFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty anything:hasPictureTitle ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasStillImageFileValue + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -329,32 +328,32 @@ anything:ThingPicture rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -364,12 +363,22 @@ anything:ThingPicture rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:hasPermissions + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; @@ -403,31 +412,26 @@ anything:ThingWithSeqnum rdfs:subClassOf anything:Thing ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 2 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasRichtext - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + salsah-gui:guiOrder 6 ; + owl:maxCardinality 1 ; + owl:onProperty anything:hasBoolean ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; + salsah-gui:guiOrder 3 ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty anything:hasDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 1 ; + salsah-gui:guiOrder 5 ; owl:minCardinality 0 ; - owl:onProperty anything:hasOtherThing + owl:onProperty anything:hasDecimal ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 15 ; - owl:minCardinality 0 ; - owl:onProperty anything:isPartOfOtherThing + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -437,30 +441,33 @@ anything:ThingWithSeqnum ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 0 ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasOtherListItem + owl:onProperty anything:hasThingPictureValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + salsah-gui:guiOrder 7 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasUri ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 12 ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasGeoname + owl:onProperty anything:hasThingDocumentValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + salsah-gui:guiOrder 12 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasGeoname ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + salsah-gui:guiOrder 11 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasGeometry ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -472,96 +479,98 @@ anything:ThingWithSeqnum knora-api:isInherited true ; salsah-gui:guiOrder 2 ; owl:minCardinality 0 ; - owl:onProperty anything:hasText + owl:onProperty anything:hasRichtext ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + salsah-gui:guiOrder 10 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasColor ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 7 ; + salsah-gui:guiOrder 0 ; owl:minCardinality 0 ; - owl:onProperty anything:hasUri + owl:onProperty anything:hasOtherListItem ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 11 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasGeometry + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 100 ; + owl:minCardinality 0 ; + owl:onProperty knora-api:seqnum ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingPicture + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 6 ; owl:maxCardinality 1 ; - owl:onProperty anything:hasBoolean + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 9 ; owl:minCardinality 0 ; - owl:onProperty anything:hasInterval + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:onProperty anything:hasTimeStamp ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 13 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasThingDocumentValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 3 ; + salsah-gui:guiOrder 4 ; owl:minCardinality 0 ; - owl:onProperty anything:hasDate + owl:onProperty anything:hasInteger ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 13 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasThingDocument + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 13 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasTimeStamp + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 100 ; - owl:minCardinality 0 ; - owl:onProperty knora-api:seqnum + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + salsah-gui:guiOrder 15 ; + owl:minCardinality 0 ; + owl:onProperty anything:isPartOfOtherThingValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + salsah-gui:guiOrder 1 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasOtherThing ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -570,58 +579,58 @@ anything:ThingWithSeqnum ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 5 ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasDecimal + owl:onProperty anything:hasThingDocument ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:onProperty anything:hasThingPicture ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 13 ; + salsah-gui:guiOrder 15 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingPictureValue + owl:onProperty anything:isPartOfOtherThing ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 15 ; + salsah-gui:guiOrder 2 ; owl:minCardinality 0 ; - owl:onProperty anything:isPartOfOtherThingValue + owl:onProperty anything:hasText ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 10 ; + salsah-gui:guiOrder 9 ; owl:minCardinality 0 ; - owl:onProperty anything:hasColor + owl:onProperty anything:hasInterval ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - salsah-gui:guiOrder 4 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasInteger + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true . @@ -685,62 +694,62 @@ anything:TrivialThing rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -749,23 +758,23 @@ anything:TrivialThing ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; @@ -815,8 +824,8 @@ anything:ThingDocument rdfs:subClassOf knora-api:DocumentRepresentation ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -825,62 +834,58 @@ anything:ThingDocument ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:userHasPermission ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty anything:hasDocumentTitle - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:cardinality 1 ; + owl:onProperty knora-api:hasDocumentFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -889,23 +894,27 @@ anything:ThingDocument ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasDocumentFileValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty anything:hasDocumentTitle ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; @@ -947,76 +956,61 @@ anything:ThingWithRegion rdfs:subClassOf knora-api:Resource ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty anything:thingHasRegion + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:deleteComment ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty anything:thingHasRegionValue + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:versionDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty anything:thingHasRegionValue + owl:onProperty anything:thingHasRegion ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1026,17 +1020,32 @@ anything:ThingWithRegion rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:isDeleted + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true . @@ -1068,198 +1077,198 @@ anything:Thing a owl:Class ; rdfs:subClassOf knora-api:Resource ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 13 ; + salsah-gui:guiOrder 9 ; owl:minCardinality 0 ; - owl:onProperty anything:hasTimeStamp - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty anything:hasInterval ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 13 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasThingPictureValue ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 5 ; + salsah-gui:guiOrder 1 ; owl:minCardinality 0 ; - owl:onProperty anything:hasDecimal + owl:onProperty anything:hasOtherThing ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 13 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasThingDocumentValue ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 3 ; + salsah-gui:guiOrder 10 ; owl:minCardinality 0 ; - owl:onProperty anything:hasDate + owl:onProperty anything:hasColor ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingPicture + owl:onProperty anything:hasThingDocument ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; + salsah-gui:guiOrder 7 ; owl:minCardinality 0 ; - owl:onProperty anything:hasOtherThing - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:onProperty anything:hasUri ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 10 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasColor + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 9 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasInterval + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 13 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasThingPictureValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 13 ; + salsah-gui:guiOrder 4 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingDocumentValue + owl:onProperty anything:hasInteger ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 0 ; owl:minCardinality 0 ; - owl:onProperty anything:hasOtherListItem + owl:onProperty anything:hasListItem ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 6 ; owl:maxCardinality 1 ; owl:onProperty anything:hasBoolean ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject + ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 15 ; + salsah-gui:guiOrder 12 ; owl:minCardinality 0 ; - owl:onProperty anything:isPartOfOtherThingValue + owl:onProperty anything:hasGeoname ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 4 ; + salsah-gui:guiOrder 11 ; owl:minCardinality 0 ; - owl:onProperty anything:hasInteger + owl:onProperty anything:hasGeometry ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; + salsah-gui:guiOrder 5 ; owl:minCardinality 0 ; - owl:onProperty anything:hasRichtext + owl:onProperty anything:hasDecimal + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 1 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasOtherThingValue ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 15 ; owl:minCardinality 0 ; owl:onProperty anything:isPartOfOtherThing ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 13 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasThingPicture + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 3 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingDocument + owl:onProperty anything:hasTimeStamp ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; + salsah-gui:guiOrder 15 ; owl:minCardinality 0 ; - owl:onProperty anything:hasText + owl:onProperty anything:isPartOfOtherThingValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 7 ; + salsah-gui:guiOrder 2 ; owl:minCardinality 0 ; - owl:onProperty anything:hasUri + owl:onProperty anything:hasText ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:deletedBy ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 0 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasListItem + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasGeoname + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 11 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasGeometry + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; + salsah-gui:guiOrder 0 ; owl:minCardinality 0 ; - owl:onProperty anything:hasOtherThingValue + owl:onProperty anything:hasOtherListItem + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 2 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasRichtext ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; @@ -1272,82 +1281,77 @@ anything:StandoffEventTag rdfs:subClassOf knora-api:StandoffDateTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndEra - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartEra + owl:maxCardinality 1 ; + owl:onProperty knora-api:dateValueHasEndDay ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasStartDay + owl:onProperty knora-api:dateValueHasStartMonth ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty anything:standoffEventTagHasDescription ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:dateValueHasEndEra ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasCalendar + owl:onProperty knora-api:dateValueHasEndYear ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:dateValueHasCalendar ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartYear + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndMonth + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasStartMonth + owl:cardinality 1 ; + owl:onProperty knora-api:dateValueHasStartEra ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndDay - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty anything:standoffEventTagHasDescription + owl:cardinality 1 ; + owl:onProperty knora-api:dateValueHasStartYear ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:dateValueHasEndMonth ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1357,17 +1361,22 @@ anything:StandoffEventTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:dateValueHasStartDay ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndYear + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; knora-api:isStandoffClass true . @@ -1452,71 +1461,67 @@ anything:ThingWithRepresentation rdfs:subClassOf knora-api:Resource ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasRepresentation + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasRepresentationValue + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1525,23 +1530,27 @@ anything:ThingWithRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:deleteComment + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasRepresentationValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true . @@ -1556,12 +1565,3 @@ anything:hasOtherThingValue knora-api:objectType knora-api:LinkValue ; knora-api:subjectType anything:Thing ; salsah-gui:guiElement salsah-gui:Searchbox . - -anything:hasGeoname a owl:ObjectProperty ; - rdfs:label "Geoname" ; - rdfs:subPropertyOf knora-api:hasValue ; - knora-api:isEditable true ; - knora-api:isResourceProperty true ; - knora-api:objectType knora-api:GeonameValue ; - knora-api:subjectType anything:Thing ; - salsah-gui:guiElement salsah-gui:Geonames . diff --git a/test_data/ontologyR2RV2/anythingThingWithAllLanguages.ttl b/test_data/ontologyR2RV2/anythingThingWithAllLanguages.ttl index 07fd2f3490..efc93e363c 100644 --- a/test_data/ontologyR2RV2/anythingThingWithAllLanguages.ttl +++ b/test_data/ontologyR2RV2/anythingThingWithAllLanguages.ttl @@ -17,55 +17,85 @@ anything:Thing a owl:Class ; rdfs:label "Chose"@fr , "Ding"@de , "Cosa"@it , "Thing"@en ; rdfs:subClassOf knora-api:Resource ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 15 ; + salsah-gui:guiOrder 3 ; owl:minCardinality 0 ; - owl:onProperty anything:isPartOfOtherThingValue + owl:onProperty anything:hasDate ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 15 ; + salsah-gui:guiOrder 10 ; owl:minCardinality 0 ; - owl:onProperty anything:isPartOfOtherThing + owl:onProperty anything:hasColor ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 13 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasTimeStamp + salsah-gui:guiOrder 6 ; + owl:maxCardinality 1 ; + owl:onProperty anything:hasBoolean ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 13 ; + salsah-gui:guiOrder 2 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingPictureValue + owl:onProperty anything:hasRichtext ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 4 ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasInteger + owl:onProperty anything:hasThingPicture + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingDocument + owl:onProperty anything:hasTimeStamp ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 0 ; owl:minCardinality 0 ; owl:onProperty anything:hasListItem ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 13 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasThingPictureValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 13 ; + owl:minCardinality 0 ; + owl:onProperty anything:hasThingDocumentValue + ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 5 ; owl:minCardinality 0 ; @@ -74,22 +104,22 @@ anything:Thing a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 9 ; + salsah-gui:guiOrder 2 ; owl:minCardinality 0 ; - owl:onProperty anything:hasInterval - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty anything:hasText ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 10 ; + salsah-gui:guiOrder 0 ; owl:minCardinality 0 ; - owl:onProperty anything:hasColor + owl:onProperty anything:hasOtherListItem + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -99,42 +129,32 @@ anything:Thing a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 13 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasThingDocumentValue + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; + salsah-gui:guiOrder 12 ; owl:minCardinality 0 ; - owl:onProperty anything:hasOtherThingValue + owl:onProperty anything:hasGeoname ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 0 ; - owl:minCardinality 0 ; - owl:onProperty anything:hasOtherListItem + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 3 ; + salsah-gui:guiOrder 4 ; owl:minCardinality 0 ; - owl:onProperty anything:hasDate + owl:onProperty anything:hasInteger ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; + salsah-gui:guiOrder 9 ; owl:minCardinality 0 ; - owl:onProperty anything:hasText + owl:onProperty anything:hasInterval ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 11 ; @@ -143,33 +163,18 @@ anything:Thing a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; + salsah-gui:guiOrder 1 ; owl:minCardinality 0 ; - owl:onProperty anything:hasGeoname + owl:onProperty anything:hasOtherThingValue ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; + salsah-gui:guiOrder 1 ; owl:minCardinality 0 ; - owl:onProperty anything:hasRichtext - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty anything:hasOtherThing ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -177,39 +182,34 @@ anything:Thing a owl:Class ; owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 7 ; + salsah-gui:guiOrder 13 ; owl:minCardinality 0 ; - owl:onProperty anything:hasUri - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty anything:hasThingDocument ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 13 ; + salsah-gui:guiOrder 15 ; owl:minCardinality 0 ; - owl:onProperty anything:hasThingPicture + owl:onProperty anything:isPartOfOtherThingValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; + salsah-gui:guiOrder 7 ; owl:minCardinality 0 ; - owl:onProperty anything:hasOtherThing + owl:onProperty anything:hasUri + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 15 ; + owl:minCardinality 0 ; + owl:onProperty anything:isPartOfOtherThing ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 6 ; - owl:maxCardinality 1 ; - owl:onProperty anything:hasBoolean + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; diff --git a/test_data/ontologyR2RV2/boxOntologyWithValueObjects.ttl b/test_data/ontologyR2RV2/boxOntologyWithValueObjects.ttl index c477a56530..9130bb5220 100644 --- a/test_data/ontologyR2RV2/boxOntologyWithValueObjects.ttl +++ b/test_data/ontologyR2RV2/boxOntologyWithValueObjects.ttl @@ -10,55 +10,60 @@ example-box:Box a owl:Class ; rdfs:comment "A shared thing." ; rdfs:label "shared thing" ; rdfs:subClassOf knora-api:Resource ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 0 ; + owl:maxCardinality 1 ; + owl:onProperty example-box:hasName + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -68,22 +73,17 @@ example-box:Box a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 0 ; - owl:maxCardinality 1 ; - owl:onProperty example-box:hasName + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -92,13 +92,13 @@ example-box:Box a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; diff --git a/test_data/ontologyR2RV2/imagesBild.ttl b/test_data/ontologyR2RV2/imagesBild.ttl index 9b726fa951..c2c41f2e58 100644 --- a/test_data/ontologyR2RV2/imagesBild.ttl +++ b/test_data/ontologyR2RV2/imagesBild.ttl @@ -15,135 +15,135 @@ images:bild a owl:Class ; rdfs:comment "An image of the demo image collection" ; rdfs:label "Image" ; rdfs:subClassOf knora-api:StillImageRepresentation ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 5 ; + owl:cardinality 1 ; + owl:onProperty images:jahrzehnt + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLinkValue ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; - owl:maxCardinality 1 ; - owl:onProperty images:urheberValue - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; - owl:maxCardinality 1 ; - owl:onProperty images:urheber + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 13 ; owl:maxCardinality 1 ; - owl:onProperty images:copyrightValue + owl:onProperty images:copyright + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 3 ; + salsah-gui:guiOrder 9 ; owl:cardinality 1 ; - owl:onProperty images:description + owl:onProperty images:mutationsdatum + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 12 ; + owl:maxCardinality 1 ; + owl:onProperty images:urheber ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 11 ; - owl:maxCardinality 1 ; - owl:onProperty images:negativnummer - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 6 ; - owl:cardinality 1 ; - owl:onProperty images:jahr_exakt + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 4 ; owl:minCardinality 1 ; owl:onProperty images:jahreszeit ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 10 ; + owl:cardinality 1 ; + owl:onProperty images:bearbeiter + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 13 ; owl:maxCardinality 1 ; - owl:onProperty images:copyright - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty images:copyrightValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:versionArkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 7 ; + owl:minCardinality 0 ; + owl:onProperty images:hatBildformat ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:creationDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 3 ; + owl:cardinality 1 ; + owl:onProperty images:description ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 8 ; + owl:cardinality 1 ; + owl:onProperty images:erfassungsdatum + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 11 ; + owl:maxCardinality 1 ; + owl:onProperty images:negativnummer ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 10 ; + salsah-gui:guiOrder 2 ; owl:cardinality 1 ; - owl:onProperty images:bearbeiter + owl:onProperty images:bildnr ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 5 ; - owl:cardinality 1 ; - owl:onProperty images:jahrzehnt + salsah-gui:guiOrder 12 ; + owl:maxCardinality 1 ; + owl:onProperty images:urheberValue ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 9 ; + salsah-gui:guiOrder 6 ; owl:cardinality 1 ; - owl:onProperty images:mutationsdatum - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty images:jahr_exakt ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:cardinality 1 ; - owl:onProperty images:bildnr + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 1 ; @@ -153,42 +153,42 @@ images:bild a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 7 ; - owl:minCardinality 0 ; - owl:onProperty images:hatBildformat + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 0 ; - owl:cardinality 1 ; - owl:onProperty images:titel + owl:onProperty knora-api:hasStillImageFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 7 ; owl:minCardinality 0 ; owl:onProperty images:hatBildformatValue ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasStillImageFileValue + owl:onProperty knora-api:arkUrl + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 8 ; + salsah-gui:guiOrder 0 ; owl:cardinality 1 ; - owl:onProperty images:erfassungsdatum + owl:onProperty images:titel ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; diff --git a/test_data/ontologyR2RV2/incunabulaBook.ttl b/test_data/ontologyR2RV2/incunabulaBook.ttl index ab4006badc..67105943cf 100644 --- a/test_data/ontologyR2RV2/incunabulaBook.ttl +++ b/test_data/ontologyR2RV2/incunabulaBook.ttl @@ -15,16 +15,6 @@ incunabula:book a owl:Class ; rdfs:comment "Diese Resource-Klasse beschreibt ein Buch" ; rdfs:label "Book" ; rdfs:subClassOf knora-api:Resource ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:hasAuthor - ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 5 ; owl:maxCardinality 1 ; @@ -33,22 +23,17 @@ incunabula:book a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:book_comment + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 6 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:location + salsah-gui:guiOrder 5 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:citation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -56,64 +41,64 @@ incunabula:book a owl:Class ; owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 4 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:publoc + salsah-gui:guiOrder 10 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:note ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 9 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:physical_desc + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 7 ; + salsah-gui:guiOrder 6 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:url + owl:onProperty incunabula:location ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 3 ; + salsah-gui:guiOrder 12 ; owl:minCardinality 0 ; - owl:onProperty incunabula:publisher + owl:onProperty incunabula:book_comment ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 4 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:publoc ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 3 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:publisher ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 2 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:hasAuthor ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -121,19 +106,29 @@ incunabula:book a owl:Class ; owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 5 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:citation + salsah-gui:guiOrder 2 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:description ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 10 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:note + salsah-gui:guiOrder 1 ; + owl:minCardinality 1 ; + owl:onProperty incunabula:title ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty knora-api:hasIncomingLinkValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 7 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:url + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -143,22 +138,27 @@ incunabula:book a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; - owl:minCardinality 1 ; - owl:onProperty incunabula:title + salsah-gui:guiOrder 9 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:physical_desc ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:description + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; diff --git a/test_data/ontologyR2RV2/incunabulaOntologySimple.ttl b/test_data/ontologyR2RV2/incunabulaOntologySimple.ttl index 74ca7caaa5..3a898bd261 100644 --- a/test_data/ontologyR2RV2/incunabulaOntologySimple.ttl +++ b/test_data/ontologyR2RV2/incunabulaOntologySimple.ttl @@ -23,73 +23,73 @@ incunabula:book a owl:Class ; rdfs:comment "Diese Resource-Klasse beschreibt ein Buch" ; rdfs:label "Book" ; rdfs:subClassOf knora-api:Resource ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty incunabula:citation ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:description ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty incunabula:physical_desc + owl:onProperty incunabula:pubdate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty incunabula:book_comment + owl:onProperty knora-api:hasIncomingLink ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty incunabula:hasAuthor ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty incunabula:note + owl:onProperty incunabula:book_comment ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:url + owl:minCardinality 0 ; + owl:onProperty incunabula:note + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:pubdate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty incunabula:description + owl:onProperty incunabula:location ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty incunabula:hasAuthor + owl:minCardinality 1 ; + owl:onProperty incunabula:title ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty incunabula:publoc - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLink + owl:onProperty incunabula:url ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty incunabula:title + owl:maxCardinality 1 ; + owl:onProperty incunabula:publoc ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty incunabula:publisher + owl:maxCardinality 1 ; + owl:onProperty incunabula:physical_desc ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty incunabula:citation - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:location + owl:onProperty incunabula:publisher ] ; knora-api:resourceIcon "book.gif" . @@ -106,19 +106,23 @@ incunabula:Sideband a owl:Class ; rdfs:subClassOf knora-api:StillImageRepresentation ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLink + owl:onProperty incunabula:sideband_comment ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasStillImageFile + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:description ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:description + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLink + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasStillImageFile ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -132,10 +136,6 @@ incunabula:Sideband a owl:Class ; owl:cardinality 1 ; owl:onProperty incunabula:sbTitle ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty incunabula:sideband_comment - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasStandoffLinkTo @@ -173,12 +173,6 @@ incunabula:miscHasGeometry knora-api:objectType knora-api:Geom ; knora-api:subjectType incunabula:misc . -incunabula:sbTitle a owl:DatatypeProperty ; - rdfs:label "Title" ; - rdfs:subPropertyOf , knora-api:hasValue ; - knora-api:objectType xsd:string ; - knora-api:subjectType incunabula:Sideband . - incunabula:hasAuthor a owl:DatatypeProperty ; rdfs:comment "Erzeuger/Autor" ; rdfs:label "Creator" ; @@ -186,6 +180,12 @@ incunabula:hasAuthor a owl:DatatypeProperty ; knora-api:objectType xsd:string ; knora-api:subjectType incunabula:book . +incunabula:sbTitle a owl:DatatypeProperty ; + rdfs:label "Title" ; + rdfs:subPropertyOf , knora-api:hasValue ; + knora-api:objectType xsd:string ; + knora-api:subjectType incunabula:Sideband . + incunabula:seqnum a owl:DatatypeProperty ; rdfs:comment "This property stands for the position within a set of rdered items (resoucres)" ; rdfs:label "Sequence number" ; @@ -296,22 +296,14 @@ incunabula:misc a owl:Class ; rdfs:comment "A fake resource class that only has optional properties" ; rdfs:label "Sonstiges" ; rdfs:subClassOf knora-api:Resource ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:miscHasGeometry ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty rdfs:label ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:miscHasColor - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLink - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasStandoffLinkTo @@ -320,83 +312,91 @@ incunabula:misc a owl:Class ; owl:cardinality 1 ; owl:onProperty knora-api:arkUrl ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLink + ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty incunabula:miscHasBook + owl:onProperty incunabula:miscHasColor ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty incunabula:miscHasGeometry + owl:onProperty incunabula:miscHasBook + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] . incunabula:page a owl:Class ; rdfs:comment "A page is a part of a book" ; rdfs:label "Page" ; rdfs:subClassOf knora-api:StillImageRepresentation ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasStillImageFile - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty incunabula:description + owl:onProperty incunabula:seqnum ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLink + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty incunabula:partOf ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty incunabula:origname + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:hasStillImageFile + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty incunabula:citation ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:hasRightSideband + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLink ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty incunabula:pagenum ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty rdfs:label - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty incunabula:partOf + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:hasRightSideband ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty incunabula:transcription ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty incunabula:page_comment + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty incunabula:citation + owl:onProperty incunabula:page_comment ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty incunabula:seqnum + owl:onProperty incunabula:hasLeftSideband ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty incunabula:hasLeftSideband + owl:onProperty incunabula:description ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasStandoffLinkTo ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty incunabula:origname + ] ; knora-api:resourceIcon "page.gif" . incunabula:publisher a owl:DatatypeProperty ; diff --git a/test_data/ontologyR2RV2/incunabulaOntologyWithValueObjects.ttl b/test_data/ontologyR2RV2/incunabulaOntologyWithValueObjects.ttl index 3b2cfee647..2dac16d8df 100644 --- a/test_data/ontologyR2RV2/incunabulaOntologyWithValueObjects.ttl +++ b/test_data/ontologyR2RV2/incunabulaOntologyWithValueObjects.ttl @@ -6,6 +6,16 @@ @prefix knora-api: . @prefix incunabula: . +incunabula:pubdate a owl:ObjectProperty ; + rdfs:comment "Datum der Herausgabe" ; + rdfs:label "Datum der Herausgabe" ; + rdfs:subPropertyOf , knora-api:hasValue ; + knora-api:isEditable true ; + knora-api:isResourceProperty true ; + knora-api:objectType knora-api:DateValue ; + knora-api:subjectType incunabula:book ; + salsah-gui:guiElement salsah-gui:Date . + incunabula:miscHasBookValue a owl:ObjectProperty ; rdfs:label "Verbindung mit einem Buch" ; @@ -100,66 +110,66 @@ incunabula:Sideband a owl:Class ; rdfs:comment "Randleistentyp" ; rdfs:label "Randleiste" ; rdfs:subClassOf knora-api:StillImageRepresentation ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 0 ; + owl:cardinality 1 ; + owl:onProperty incunabula:sbTitle ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasStillImageFileValue + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:description + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 0 ; - owl:cardinality 1 ; - owl:onProperty incunabula:sbTitle + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:hasPermissions + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:attachedToProject ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser + ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 2 ; owl:minCardinality 0 ; @@ -167,18 +177,8 @@ incunabula:Sideband a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -192,8 +192,18 @@ incunabula:Sideband a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty rdfs:label + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 1 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:description + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -202,8 +212,8 @@ incunabula:Sideband a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty knora-api:hasStillImageFileValue ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true . @@ -349,109 +359,109 @@ incunabula:misc a owl:Class ; rdfs:subClassOf knora-api:Resource ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:miscHasGeometry - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 0 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:miscHasColor + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:hasPermissions ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 2 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:miscHasBookValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 2 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:miscHasBook ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; + salsah-gui:guiOrder 0 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:miscHasBookValue + owl:onProperty incunabula:miscHasColor ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:miscHasBook + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty rdfs:label ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 1 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:miscHasGeometry + ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true . @@ -459,160 +469,160 @@ incunabula:page a owl:Class ; rdfs:comment "A page is a part of a book" ; rdfs:label "Page" ; rdfs:subClassOf knora-api:StillImageRepresentation ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 10 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:hasLeftSidebandValue - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 5 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:citation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:versionDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 6 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:page_comment + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 1 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:pagenum ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 10 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:hasLeftSideband + owl:onProperty incunabula:hasLeftSidebandValue ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; + salsah-gui:guiOrder 2 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:pagenum + owl:onProperty incunabula:description + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasStillImageFileValue + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 6 ; + salsah-gui:guiOrder 12 ; owl:minCardinality 0 ; - owl:onProperty incunabula:page_comment + owl:onProperty incunabula:transcription + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 7 ; + owl:cardinality 1 ; + owl:onProperty incunabula:origname ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLinkValue ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:cardinality 1 ; - owl:onProperty incunabula:partOfValue - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:description - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 11 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:hasRightSidebandValue + owl:onProperty incunabula:hasRightSideband ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 11 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:hasRightSideband + salsah-gui:guiOrder 2 ; + owl:cardinality 1 ; + owl:onProperty incunabula:partOfValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 7 ; + salsah-gui:guiOrder 2 ; owl:cardinality 1 ; - owl:onProperty incunabula:origname + owl:onProperty incunabula:partOf ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:deletedBy ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:cardinality 1 ; - owl:onProperty incunabula:partOf - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:transcription + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasStillImageFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:arkUrl ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 10 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:hasLeftSideband + ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 3 ; owl:maxCardinality 1 ; owl:onProperty incunabula:seqnum ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 11 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:hasRightSidebandValue + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 5 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:citation + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:attachedToProject ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; @@ -658,21 +668,6 @@ incunabula:book a owl:Class ; rdfs:comment "Diese Resource-Klasse beschreibt ein Buch" ; rdfs:label "Book" ; rdfs:subClassOf knora-api:Resource ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:description - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 5 ; owl:minCardinality 0 ; @@ -680,88 +675,83 @@ incunabula:book a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:attachedToUser ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 7 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:url ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; - owl:minCardinality 1 ; - owl:onProperty incunabula:title + salsah-gui:guiOrder 2 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:hasAuthor ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 3 ; + salsah-gui:guiOrder 12 ; owl:minCardinality 0 ; - owl:onProperty incunabula:publisher - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty incunabula:book_comment ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 5 ; owl:maxCardinality 1 ; owl:onProperty incunabula:pubdate ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 6 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:location + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; + salsah-gui:guiOrder 3 ; owl:minCardinality 0 ; - owl:onProperty incunabula:hasAuthor + owl:onProperty incunabula:publisher ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 4 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:publoc + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 9 ; @@ -773,35 +763,55 @@ incunabula:book a owl:Class ; owl:minCardinality 0 ; owl:onProperty incunabula:note ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 6 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:location - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:book_comment + salsah-gui:guiOrder 1 ; + owl:minCardinality 1 ; + owl:onProperty incunabula:title ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 7 ; + salsah-gui:guiOrder 4 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:url + owl:onProperty incunabula:publoc + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 2 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:description + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:hasPermissions + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; @@ -844,17 +854,6 @@ incunabula:page_comment salsah-gui:guiAttribute "rows=7" , "wrap=soft" , "width=95%" ; salsah-gui:guiElement salsah-gui:Textarea . -incunabula:transcription - a owl:ObjectProperty ; - rdfs:comment "Transkription" ; - rdfs:label "Transkription" ; - rdfs:subPropertyOf knora-api:hasValue ; - knora-api:isEditable true ; - knora-api:isResourceProperty true ; - knora-api:objectType knora-api:TextValue ; - knora-api:subjectType incunabula:page ; - salsah-gui:guiElement salsah-gui:Richtext . - incunabula:title a owl:ObjectProperty ; rdfs:comment "Titel" ; rdfs:label "Title" ; @@ -866,12 +865,13 @@ incunabula:title a owl:ObjectProperty ; salsah-gui:guiAttribute "size=80" , "maxlength=255" ; salsah-gui:guiElement salsah-gui:SimpleText . -incunabula:pubdate a owl:ObjectProperty ; - rdfs:comment "Datum der Herausgabe" ; - rdfs:label "Datum der Herausgabe" ; - rdfs:subPropertyOf , knora-api:hasValue ; +incunabula:transcription + a owl:ObjectProperty ; + rdfs:comment "Transkription" ; + rdfs:label "Transkription" ; + rdfs:subPropertyOf knora-api:hasValue ; knora-api:isEditable true ; knora-api:isResourceProperty true ; - knora-api:objectType knora-api:DateValue ; - knora-api:subjectType incunabula:book ; - salsah-gui:guiElement salsah-gui:Date . + knora-api:objectType knora-api:TextValue ; + knora-api:subjectType incunabula:page ; + salsah-gui:guiElement salsah-gui:Richtext . diff --git a/test_data/ontologyR2RV2/incunabulaPage.ttl b/test_data/ontologyR2RV2/incunabulaPage.ttl index 119945ad4c..733d04ffed 100644 --- a/test_data/ontologyR2RV2/incunabulaPage.ttl +++ b/test_data/ontologyR2RV2/incunabulaPage.ttl @@ -15,16 +15,6 @@ incunabula:page a owl:Class ; rdfs:comment "A page is a part of a book" ; rdfs:label "Page" ; rdfs:subClassOf knora-api:StillImageRepresentation ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 7 ; - owl:cardinality 1 ; - owl:onProperty incunabula:origname - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 6 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:page_comment - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; @@ -32,133 +22,143 @@ incunabula:page a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:cardinality 1 ; - owl:onProperty incunabula:partOf + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 5 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:citation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 11 ; + salsah-gui:guiOrder 3 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:hasRightSidebandValue + owl:onProperty incunabula:seqnum ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; + salsah-gui:guiOrder 2 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:pagenum + owl:onProperty incunabula:description ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasStillImageFileValue - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 5 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:citation + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty knora-api:versionArkUrl + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 2 ; + owl:cardinality 1 ; + owl:onProperty incunabula:partOf + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 10 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:hasLeftSideband ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 2 ; owl:cardinality 1 ; owl:onProperty incunabula:partOfValue ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 11 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:hasRightSideband + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 11 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:hasRightSideband + salsah-gui:guiOrder 12 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:transcription ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 10 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:hasLeftSideband + owl:onProperty incunabula:hasLeftSidebandValue ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 3 ; + salsah-gui:guiOrder 7 ; + owl:cardinality 1 ; + owl:onProperty incunabula:origname + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 1 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:seqnum + owl:onProperty incunabula:pagenum ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:hasStillImageFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 10 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:hasLeftSidebandValue - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:description + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; + salsah-gui:guiOrder 11 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:hasRightSidebandValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 6 ; owl:minCardinality 0 ; - owl:onProperty incunabula:transcription + owl:onProperty incunabula:page_comment + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -168,7 +168,7 @@ incunabula:page a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:lastModificationDate ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; diff --git a/test_data/ontologyR2RV2/incunabulaPageAndBookWithValueObjects.ttl b/test_data/ontologyR2RV2/incunabulaPageAndBookWithValueObjects.ttl index 41f7750dd3..94d2fef2e3 100644 --- a/test_data/ontologyR2RV2/incunabulaPageAndBookWithValueObjects.ttl +++ b/test_data/ontologyR2RV2/incunabulaPageAndBookWithValueObjects.ttl @@ -10,30 +10,35 @@ incunabula:page a owl:Class ; rdfs:comment "A page is a part of a book" ; rdfs:label "Page" ; rdfs:subClassOf knora-api:StillImageRepresentation ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 5 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:citation - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 10 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:hasLeftSideband + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 2 ; - owl:cardinality 1 ; - owl:onProperty incunabula:partOf + owl:maxCardinality 1 ; + owl:onProperty incunabula:description ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; + salsah-gui:guiOrder 3 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:pagenum + owl:onProperty incunabula:seqnum + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 7 ; @@ -43,77 +48,72 @@ incunabula:page a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 2 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:description + owl:cardinality 1 ; + owl:onProperty incunabula:partOfValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 12 ; owl:minCardinality 0 ; owl:onProperty incunabula:transcription ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 11 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:hasRightSidebandValue + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:cardinality 1 ; - owl:onProperty incunabula:partOfValue + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 11 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:hasRightSideband + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 10 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:hasLeftSidebandValue + owl:onProperty incunabula:hasLeftSideband ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:versionDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 1 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:pagenum ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 5 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:citation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasStillImageFileValue - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -122,48 +122,48 @@ incunabula:page a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 6 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:page_comment + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 6 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:page_comment ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 11 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:hasRightSideband ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 10 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:hasLeftSidebandValue ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 3 ; + salsah-gui:guiOrder 2 ; + owl:cardinality 1 ; + owl:onProperty incunabula:partOf + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 11 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:seqnum + owl:onProperty incunabula:hasRightSidebandValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:hasStillImageFileValue ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; @@ -173,150 +173,150 @@ incunabula:book a owl:Class ; rdfs:comment "Diese Resource-Klasse beschreibt ein Buch" ; rdfs:label "Book" ; rdfs:subClassOf knora-api:Resource ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 4 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:publoc + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 5 ; + salsah-gui:guiOrder 2 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:pubdate + owl:onProperty incunabula:description ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; owl:onProperty knora-api:hasStandoffLinkToValue ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:attachedToProject + ] ; + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 2 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:hasAuthor ] ; rdfs:subClassOf [ a owl:Restriction ; salsah-gui:guiOrder 5 ; owl:minCardinality 0 ; owl:onProperty incunabula:citation ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 12 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:book_comment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLinkValue ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 6 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:location ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 7 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:url ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:isDeleted ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + rdfs:subClassOf [ a owl:Restriction ; + salsah-gui:guiOrder 9 ; + owl:maxCardinality 1 ; + owl:onProperty incunabula:physical_desc ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 1 ; - owl:minCardinality 1 ; - owl:onProperty incunabula:title + salsah-gui:guiOrder 10 ; + owl:minCardinality 0 ; + owl:onProperty incunabula:note ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 9 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:physical_desc + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 3 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:publisher + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:versionArkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 10 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:note - ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 7 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:url + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 12 ; + salsah-gui:guiOrder 3 ; owl:minCardinality 0 ; - owl:onProperty incunabula:book_comment + owl:onProperty incunabula:publisher ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:minCardinality 0 ; - owl:onProperty incunabula:hasAuthor + salsah-gui:guiOrder 1 ; + owl:minCardinality 1 ; + owl:onProperty incunabula:title ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 2 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:description + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; - rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 4 ; - owl:maxCardinality 1 ; - owl:onProperty incunabula:publoc + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - salsah-gui:guiOrder 6 ; + salsah-gui:guiOrder 5 ; owl:maxCardinality 1 ; - owl:onProperty incunabula:location + owl:onProperty incunabula:pubdate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; diff --git a/test_data/ontologyR2RV2/knoraApiDateValue.ttl b/test_data/ontologyR2RV2/knoraApiDateValue.ttl index c09aa79e80..e0132a26e8 100644 --- a/test_data/ontologyR2RV2/knoraApiDateValue.ttl +++ b/test_data/ontologyR2RV2/knoraApiDateValue.ttl @@ -21,107 +21,107 @@ knora-api:DateValue a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasStartDay - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:dateValueHasStartYear ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:dateValueHasEndEra ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasStartMonth + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndMonth + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:dateValueHasEndYear ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:dateValueHasEndDay ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:dateValueHasStartMonth ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:dateValueHasEndMonth ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartYear + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndYear + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasCalendar + owl:onProperty knora-api:dateValueHasStartEra ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndDay + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:dateValueHasStartDay ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndEra + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:dateValueHasCalendar + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartEra + owl:onProperty knora-api:userHasPermission ] ; knora-api:isValueClass true . diff --git a/test_data/ontologyR2RV2/knoraApiOntologySimple.ttl b/test_data/ontologyR2RV2/knoraApiOntologySimple.ttl index f0d772ce9d..8a4c9ded09 100644 --- a/test_data/ontologyR2RV2/knoraApiOntologySimple.ttl +++ b/test_data/ontologyR2RV2/knoraApiOntologySimple.ttl @@ -4,22 +4,34 @@ @prefix rdfs: . @prefix knora-api: . +knora-api:hasMovingImageFile + a owl:DatatypeProperty ; + rdfs:comment "Connects a Representation to a movie file" ; + rdfs:label "has movie file" ; + rdfs:subPropertyOf knora-api:hasFile ; + knora-api:objectType knora-api:File ; + knora-api:subjectType knora-api:MovingImageRepresentation . + knora-api:TextRepresentation a owl:Class ; rdfs:comment "A resource containing a text file" ; rdfs:label "Representation (Text)" ; rdfs:subClassOf knora-api:Representation ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasTextFile + ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLink ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:hasTextFile + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; @@ -27,11 +39,7 @@ knora-api:TextRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:arkUrl ] . knora-api:hasStandoffLinkTo @@ -57,27 +65,27 @@ knora-api:StillImageRepresentation rdfs:subClassOf knora-api:Representation ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty knora-api:hasIncomingLink ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:versionArkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:hasStillImageFile ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo + ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty rdfs:label ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLink + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] . knora-api:ListNode a rdfs:Datatype ; @@ -101,23 +109,23 @@ knora-api:DDDRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty rdfs:label ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:hasDDDFile ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo + ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:arkUrl ] . knora-api:AudioRepresentation @@ -125,9 +133,9 @@ knora-api:AudioRepresentation rdfs:comment "Represents a file containing audio data" ; rdfs:label "Representation (Audio)" ; rdfs:subClassOf knora-api:Representation ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLink ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -135,19 +143,19 @@ knora-api:AudioRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasStandoffLinkTo ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLink + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:arkUrl ] . knora-api:seqnum a owl:DatatypeProperty ; @@ -179,11 +187,7 @@ knora-api:Representation rdfs:subClassOf knora-api:Resource ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; @@ -195,11 +199,15 @@ knora-api:Representation ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty rdfs:label + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] . knora-api:Geom a rdfs:Datatype ; @@ -211,33 +219,33 @@ knora-api:Region a owl:Class ; rdfs:comment "Represents a geometric region of a resource. The geometry is represented currently as JSON string." ; rdfs:label "Region" ; rdfs:subClassOf knora-api:Resource ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:isRegionOf - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:versionArkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasColor - ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:minCardinality 1 ; + owl:onProperty knora-api:hasComment ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLink ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:isRegionOf + ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 1 ; owl:onProperty knora-api:hasGeometry ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasColor + ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty knora-api:hasComment + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -346,33 +354,33 @@ knora-api:LinkObj a owl:Class ; rdfs:comment "Represents a generic link object" ; rdfs:label "Link Object" ; rdfs:subClassOf knora-api:Resource ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasComment - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:versionArkUrl + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLink ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty rdfs:label + ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 1 ; owl:onProperty knora-api:hasLinkTo ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasComment ] ; knora-api:resourceIcon "link.gif" . @@ -383,7 +391,7 @@ knora-api:XSLTransformation rdfs:subClassOf knora-api:TextRepresentation ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:hasTextFile ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -397,13 +405,13 @@ knora-api:XSLTransformation owl:cardinality 1 ; owl:onProperty rdfs:label ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl + ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLink - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasTextFile ] . knora-api:Date a rdfs:Datatype ; @@ -418,6 +426,13 @@ knora-api:Interval a rdfs:Datatype ; owl:onDatatype xsd:string ; owl:withRestrictions [ xsd:pattern "\\d+(\\.\\d+)?,\\d+(\\.\\d+)?" ] . +knora-api:hasLinkTo a owl:ObjectProperty ; + rdfs:comment "Represents a direct connection between two resources" ; + rdfs:label "has Link to" ; + rdfs:subPropertyOf knora-api:resourceProperty ; + knora-api:objectType knora-api:Resource ; + knora-api:subjectType knora-api:Resource . + knora-api:hasAudioFile a owl:DatatypeProperty ; rdfs:comment "Connects a Representation to an audio file" ; @@ -426,13 +441,6 @@ knora-api:hasAudioFile knora-api:objectType knora-api:File ; knora-api:subjectType knora-api:AudioRepresentation . -knora-api:hasLinkTo a owl:ObjectProperty ; - rdfs:comment "Represents a direct connection between two resources" ; - rdfs:label "has Link to" ; - rdfs:subPropertyOf knora-api:resourceProperty ; - knora-api:objectType knora-api:Resource ; - knora-api:subjectType knora-api:Resource . - knora-api:hasValue a owl:DatatypeProperty ; rdfs:comment "The base property of properties that point from Knora resources to Knora values." ; rdfs:label "has value" ; @@ -490,9 +498,13 @@ knora-api:Annotation a owl:Class ; rdfs:comment "A generic class for representing annotations" ; rdfs:label "Annotation" ; rdfs:subClassOf knora-api:Resource ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl + ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLink + owl:minCardinality 1 ; + owl:onProperty knora-api:isAnnotationOf ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -500,23 +512,19 @@ knora-api:Annotation a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 1 ; - owl:onProperty knora-api:isAnnotationOf + owl:onProperty knora-api:hasComment ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:onProperty knora-api:hasIncomingLink ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty knora-api:hasComment - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] . knora-api:versionArkUrl @@ -547,11 +555,11 @@ knora-api:MovingImageRepresentation rdfs:subClassOf knora-api:Representation ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLink + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:hasMovingImageFile + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -559,15 +567,15 @@ knora-api:MovingImageRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:hasMovingImageFile + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLink ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty rdfs:label - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo ] . knora-api:arkUrl a owl:DatatypeProperty ; @@ -606,11 +614,3 @@ knora-api:resourceIcon a owl:DatatypeProperty ; knora-api:objectType xsd:string ; knora-api:subjectType owl:Class . - -knora-api:hasMovingImageFile - a owl:DatatypeProperty ; - rdfs:comment "Connects a Representation to a movie file" ; - rdfs:label "has movie file" ; - rdfs:subPropertyOf knora-api:hasFile ; - knora-api:objectType knora-api:File ; - knora-api:subjectType knora-api:MovingImageRepresentation . diff --git a/test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.ttl b/test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.ttl index 205eb7f13b..be646c6149 100644 --- a/test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.ttl +++ b/test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.ttl @@ -17,32 +17,37 @@ knora-api:DDDFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:attachedToUser + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:fileValueAsUrl + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:cardinality 1 ; + owl:onProperty knora-api:fileValueAsUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -51,18 +56,13 @@ knora-api:DDDFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:fileValueHasFilename + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -72,7 +72,7 @@ knora-api:DDDFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:fileValueHasFilename ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -87,7 +87,7 @@ knora-api:DDDFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:valueCreationDate ] ; knora-api:isValueClass true . @@ -100,45 +100,45 @@ knora-api:versionDate knora-api:StandoffTag a owl:Class ; rdfs:comment "Represents a standoff markup tag" ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart + ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasStartIndex ] ; knora-api:isStandoffClass true . @@ -150,91 +150,91 @@ knora-api:DDDRepresentation rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasDDDFileValue + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:lastModificationDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasDDDFileValue ] ; knora-api:isResourceClass true . @@ -266,41 +266,32 @@ knora-api:TextValue a owl:Class ; owl:maxCardinality 1 ; owl:onProperty knora-api:deleteComment ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString - ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:textValueAsXml - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:textValueHasMarkup ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:textValueHasStandoff + owl:maxCardinality 1 ; + owl:onProperty knora-api:textValueAsHtml ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty knora-api:textValueHasLanguage ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:textValueAsHtml + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; @@ -309,7 +300,17 @@ knora-api:TextValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:versionArkUrl + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; @@ -317,8 +318,8 @@ knora-api:TextValue a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -328,17 +329,17 @@ knora-api:TextValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -346,13 +347,12 @@ knora-api:TextValue a owl:Class ; owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:textValueHasMarkup + owl:minCardinality 0 ; + owl:onProperty knora-api:textValueHasStandoff ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:textValueAsXml ] ; knora-api:isValueClass true . @@ -392,66 +392,66 @@ knora-api:Annotation a owl:Class ; rdfs:subClassOf knora-api:Resource ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 1 ; + owl:onProperty knora-api:hasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:onProperty knora-api:hasIncomingLinkValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:lastModificationDate ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 1 ; + owl:onProperty knora-api:isAnnotationOfValue + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:versionArkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty knora-api:isAnnotationOf - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty knora-api:hasComment + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -461,31 +461,31 @@ knora-api:Annotation a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:attachedToProject + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 1 ; + owl:onProperty knora-api:isAnnotationOf ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty knora-api:isAnnotationOfValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true . @@ -514,59 +514,60 @@ knora-api:LinkValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:linkValueHasSourceIri + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:linkValueHasTarget + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:linkValueHasTargetIri + owl:onProperty knora-api:linkValueHasSourceIri ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:valueCreationDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:linkValueHasTargetIri ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; @@ -574,23 +575,22 @@ knora-api:LinkValue a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:deleteComment + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:linkValueHasTarget ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; knora-api:isValueClass true . @@ -607,20 +607,35 @@ knora-api:Representation rdfs:comment "A resource that can store a file" ; rdfs:label "Representation" ; rdfs:subClassOf knora-api:Resource ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasFileValue + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:isDeleted + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:userHasPermission ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; @@ -629,23 +644,13 @@ knora-api:Representation rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:arkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; @@ -653,23 +658,23 @@ knora-api:Representation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -678,13 +683,8 @@ knora-api:Representation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -693,8 +693,8 @@ knora-api:Representation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; knora-api:isResourceClass true . @@ -704,18 +704,13 @@ knora-api:StandoffDataTypeTag rdfs:subClassOf knora-api:StandoffTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -725,22 +720,27 @@ knora-api:StandoffDataTypeTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -749,8 +749,8 @@ knora-api:StandoffDataTypeTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; knora-api:isStandoffClass true . @@ -769,12 +769,7 @@ knora-api:TextFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -784,27 +779,27 @@ knora-api:TextFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:fileValueHasFilename ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:fileValueAsUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:fileValueHasFilename + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -813,33 +808,38 @@ knora-api:TextFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:fileValueAsUrl + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:valueAsString + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; knora-api:isValueClass true . @@ -872,42 +872,42 @@ knora-api:StandoffIntervalTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:intervalValueHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:intervalValueHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -917,17 +917,17 @@ knora-api:StandoffIntervalTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:intervalValueHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:intervalValueHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; knora-api:isStandoffClass true . @@ -942,6 +942,10 @@ knora-api:DecimalBase knora-api:DocumentFileValue a owl:Class ; rdfs:subClassOf knora-api:FileValue ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:documentFileValueHasPageCount + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; @@ -949,13 +953,13 @@ knora-api:DocumentFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -965,35 +969,31 @@ knora-api:DocumentFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:hasPermissions + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:documentFileValueHasDimX ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:documentFileValueHasPageCount - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:documentFileValueHasDimY + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1002,13 +1002,17 @@ knora-api:DocumentFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:fileValueHasFilename + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:documentFileValueHasDimY ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:fileValueHasFilename + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1018,16 +1022,12 @@ knora-api:DocumentFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:documentFileValueHasDimX + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; knora-api:isValueClass true . @@ -1049,39 +1049,44 @@ knora-api:DocumentRepresentation a owl:Class ; rdfs:label "Representation (Document)" ; rdfs:subClassOf knora-api:Representation ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:hasDocumentFileValue ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1090,53 +1095,48 @@ knora-api:DocumentRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLinkValue ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; knora-api:isResourceClass true . @@ -1157,17 +1157,17 @@ knora-api:IntValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:intValueAsInt ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1177,7 +1177,7 @@ knora-api:IntValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1186,18 +1186,13 @@ knora-api:IntValue a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1207,91 +1202,96 @@ knora-api:IntValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:valueAsString + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:intValueAsInt + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; knora-api:isValueClass true . -knora-api:author a owl:ObjectProperty ; - rdfs:comment "Specifies the author of a particular version of a resource." ; - rdfs:label "author" ; - knora-api:objectType knora-api:User . - knora-api:StandoffDecimalTag a owl:Class ; rdfs:comment "Represents a decimal (floating point) value in a TextValue" ; rdfs:subClassOf knora-api:StandoffDataTypeTag , knora-api:DecimalBase ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:decimalValueAsDecimal + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:decimalValueAsDecimal ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; knora-api:isStandoffClass true . +knora-api:author a owl:ObjectProperty ; + rdfs:comment "Specifies the author of a particular version of a resource." ; + rdfs:label "author" ; + knora-api:objectType knora-api:User . + a owl:Ontology ; rdfs:label "The knora-api ontology in the complex schema" ; @@ -1328,11 +1328,6 @@ knora-api:IntervalBase owl:onProperty knora-api:intervalValueHasEnd ] . -knora-api:resourceIcon - a owl:DatatypeProperty ; - knora-api:objectType xsd:string ; - knora-api:subjectType owl:Class . - knora-api:hasColor a owl:ObjectProperty ; rdfs:comment "Specifies the color of a region." ; rdfs:label "Color" ; @@ -1344,71 +1339,76 @@ knora-api:hasColor a owl:ObjectProperty ; salsah-gui:guiAttribute "ncolors=8" ; salsah-gui:guiElement salsah-gui:Colorpicker . +knora-api:resourceIcon + a owl:DatatypeProperty ; + knora-api:objectType xsd:string ; + knora-api:subjectType owl:Class . + knora-api:ListValue a owl:Class ; rdfs:subClassOf knora-api:Value ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:listValueAsListNode + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:isDeleted ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:listValueAsListNode + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1424,7 +1424,12 @@ knora-api:StandoffIntegerTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:intValueAsInt + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1438,18 +1443,18 @@ knora-api:StandoffIntegerTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1459,22 +1464,17 @@ knora-api:StandoffIntegerTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:intValueAsInt + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasStartIndex ] ; knora-api:isStandoffClass true . @@ -1526,28 +1526,23 @@ knora-api:StandoffDateTag rdfs:subClassOf knora-api:DateBase , knora-api:StandoffDataTypeTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndEra + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndYear + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartYear + owl:onProperty knora-api:dateValueHasEndYear ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1557,17 +1552,17 @@ knora-api:StandoffDateTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:dateValueHasStartEra ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndMonth + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:dateValueHasEndDay ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1577,47 +1572,52 @@ knora-api:StandoffDateTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasCalendar + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndDay + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:dateValueHasCalendar ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:dateValueHasEndMonth ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:dateValueHasStartYear ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:dateValueHasEndEra ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartEra + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStartIndex ] ; knora-api:isStandoffClass true . @@ -1626,21 +1626,6 @@ knora-api:StillImageRepresentation rdfs:comment "A resource that can contain a two-dimensional still image file" ; rdfs:label "Representation (Image)" ; rdfs:subClassOf knora-api:Representation ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; @@ -1653,13 +1638,23 @@ knora-api:StillImageRepresentation rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:versionArkUrl ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; @@ -1668,17 +1663,17 @@ knora-api:StillImageRepresentation rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1688,32 +1683,37 @@ knora-api:StillImageRepresentation rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:onProperty knora-api:hasStandoffLinkToValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:deleteComment ] ; knora-api:isResourceClass true . @@ -1747,13 +1747,17 @@ knora-api:StandoffInternalReferenceTag rdfs:subClassOf knora-api:StandoffDataTypeTag , knora-api:ValueBase ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasInternalReference ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1765,39 +1769,35 @@ knora-api:StandoffInternalReferenceTag owl:cardinality 1 ; owl:onProperty knora-api:standoffTagHasStart ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasInternalReference - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; knora-api:isStandoffClass true . @@ -1813,28 +1813,28 @@ knora-api:ColorValue a owl:Class ; rdfs:subClassOf knora-api:Value , knora-api:ColorBase ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:colorValueAsColor + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:colorValueAsColor ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1844,22 +1844,22 @@ knora-api:ColorValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1873,13 +1873,13 @@ knora-api:ColorValue a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; knora-api:isValueClass true . @@ -1902,24 +1902,34 @@ knora-api:audioFileValueHasDuration knora-api:objectType xsd:decimal ; knora-api:subjectType knora-api:AudioFileValue . +knora-api:valueCreationDate + a owl:DatatypeProperty ; + rdfs:subPropertyOf knora-api:valueHas ; + knora-api:objectType xsd:dateTimeStamp ; + knora-api:subjectType knora-api:Value . + knora-api:StillImageFileValue a owl:Class ; rdfs:comment "A file containing a two-dimensional still image" ; rdfs:subClassOf knora-api:FileValue ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:stillImageFileValueHasDimX ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1934,73 +1944,63 @@ knora-api:StillImageFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:fileValueHasFilename ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:stillImageFileValueHasDimX + owl:onProperty knora-api:stillImageFileValueHasDimY ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:fileValueHasFilename + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:stillImageFileValueHasDimY + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:stillImageFileValueHasIIIFBaseUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; knora-api:isValueClass true . -knora-api:valueCreationDate - a owl:DatatypeProperty ; - rdfs:subPropertyOf knora-api:valueHas ; - knora-api:objectType xsd:dateTimeStamp ; - knora-api:subjectType knora-api:Value . - knora-api:mayHaveMoreResults a owl:DatatypeProperty ; rdfs:comment "Indicates whether more results may be available for a search query" ; @@ -2073,30 +2073,27 @@ knora-api:MovingImageFileValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:fileValueHasFilename ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:movingImageFileValueHasFps - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:movingImageFileValueHasDimX + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:valueHasComment + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -2105,56 +2102,59 @@ knora-api:MovingImageFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:fileValueAsUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty knora-api:movingImageFileValueHasDimY ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:movingImageFileValueHasFps ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:movingImageFileValueHasDuration + owl:onProperty knora-api:movingImageFileValueHasDimX ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:fileValueHasFilename + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:movingImageFileValueHasDuration ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:fileValueAsUrl ] ; knora-api:isValueClass true . @@ -2181,6 +2181,11 @@ knora-api:StandoffBooleanTag a owl:Class ; rdfs:comment "Represents a boolean in a TextValue" ; rdfs:subClassOf knora-api:BooleanBase , knora-api:StandoffDataTypeTag ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; @@ -2188,8 +2193,8 @@ knora-api:StandoffBooleanTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -2204,37 +2209,32 @@ knora-api:StandoffBooleanTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:booleanValueAsBoolean ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:booleanValueAsBoolean + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; knora-api:isStandoffClass true . @@ -2243,39 +2243,30 @@ knora-api:AudioRepresentation rdfs:comment "Represents a file containing audio data" ; rdfs:label "Representation (Audio)" ; rdfs:subClassOf knora-api:Representation ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasAudioFileValue + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -2284,24 +2275,23 @@ knora-api:AudioRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; owl:onProperty knora-api:hasStandoffLinkToValue ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasAudioFileValue + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; @@ -2309,38 +2299,41 @@ knora-api:AudioRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:attachedToProject ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:onProperty knora-api:hasStandoffLinkTo + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; knora-api:isResourceClass true . -knora-api:valueHasUUID - a owl:DatatypeProperty ; - rdfs:comment "The UUID of a value" ; - rdfs:subPropertyOf knora-api:valueHas ; - knora-api:objectType xsd:string ; - knora-api:subjectType knora-api:Value . - knora-api:MovingImageRepresentation a owl:Class ; rdfs:comment "A resource containing moving image data" ; @@ -2351,49 +2344,45 @@ knora-api:MovingImageRepresentation owl:cardinality 1 ; owl:onProperty knora-api:attachedToProject ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasMovingImageFileValue - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -2402,41 +2391,52 @@ knora-api:MovingImageRepresentation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:attachedToUser + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasMovingImageFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; knora-api:isResourceClass true . +knora-api:valueHasUUID + a owl:DatatypeProperty ; + rdfs:comment "The UUID of a value" ; + rdfs:subPropertyOf knora-api:valueHas ; + knora-api:objectType xsd:string ; + knora-api:subjectType knora-api:Value . + knora-api:movingImageFileValueHasFps a owl:DatatypeProperty ; rdfs:comment "The number of frames per second in a moving image file value." ; @@ -2475,28 +2475,28 @@ knora-api:AudioFileValue rdfs:subClassOf knora-api:FileValue ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:fileValueHasFilename ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -2505,52 +2505,52 @@ knora-api:AudioFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:audioFileValueHasDuration + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:fileValueAsUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:audioFileValueHasDuration ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:fileValueHasFilename + owl:onProperty knora-api:fileValueAsUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID ] ; knora-api:isValueClass true . @@ -2634,27 +2634,27 @@ knora-api:StandoffTimeTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:timeValueAsTimeStamp + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -2664,27 +2664,27 @@ knora-api:StandoffTimeTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:timeValueAsTimeStamp ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasStartIndex ] ; knora-api:isStandoffClass true . @@ -2703,17 +2703,21 @@ knora-api:arkUrl a owl:DatatypeProperty ; knora-api:Value a owl:Class ; rdfs:comment "The base class of classes representing Knora values" ; rdfs:subClassOf knora-api:ValueBase ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:userHasPermission + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; @@ -2721,31 +2725,27 @@ knora-api:Value a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:attachedToUser ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; @@ -2753,7 +2753,7 @@ knora-api:Value a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:isDeleted ] ; knora-api:isValueClass true . @@ -2763,18 +2763,18 @@ knora-api:StandoffUriTag rdfs:subClassOf knora-api:UriBase , knora-api:StandoffDataTypeTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -2783,38 +2783,38 @@ knora-api:StandoffUriTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:uriValueAsUri ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:uriValueAsUri + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; knora-api:isStandoffClass true . @@ -2899,28 +2899,28 @@ knora-api:BooleanValue rdfs:subClassOf knora-api:Value , knora-api:BooleanBase ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:booleanValueAsBoolean + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -2935,37 +2935,37 @@ knora-api:BooleanValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:booleanValueAsBoolean ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; knora-api:isValueClass true . @@ -2974,59 +2974,54 @@ knora-api:XSLTransformation rdfs:comment "a TextRepresentation representing an XSL transformation that can be applied to an XML created from standoff. The transformation's result is ecptected to be HTML." ; rdfs:label "a TextRepresentation representing an XSL transformation that can be applied to an XML created from standoff. The transformation's result is ecptected to be HTML." ; rdfs:subClassOf knora-api:TextRepresentation ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasTextFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasTextFileValue + owl:cardinality 1 ; + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -3035,33 +3030,38 @@ knora-api:XSLTransformation ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; knora-api:isResourceClass true . @@ -3086,43 +3086,43 @@ knora-api:StandoffColorTag rdfs:subClassOf knora-api:ColorBase , knora-api:StandoffDataTypeTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:colorValueAsColor + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:colorValueAsColor ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -3132,12 +3132,12 @@ knora-api:StandoffColorTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; knora-api:isStandoffClass true . @@ -3174,14 +3174,6 @@ knora-api:isLinkValueProperty knora-api:objectType xsd:boolean ; knora-api:subjectType owl:ObjectProperty . -knora-api:dateValueHasStartDay - a owl:DatatypeProperty ; - rdfs:comment "Represents the start day of a date value." ; - rdfs:label "Date value has start day" ; - rdfs:subPropertyOf knora-api:valueHas ; - knora-api:objectType xsd:integer ; - knora-api:subjectType knora-api:DateBase . - knora-api:geonameValueAsGeonameCode a owl:DatatypeProperty ; rdfs:comment "Represents the literal Geoname code of a GeonameValue." ; @@ -3190,6 +3182,14 @@ knora-api:geonameValueAsGeonameCode knora-api:objectType xsd:string ; knora-api:subjectType knora-api:GeonameValue . +knora-api:dateValueHasStartDay + a owl:DatatypeProperty ; + rdfs:comment "Represents the start day of a date value." ; + rdfs:label "Date value has start day" ; + rdfs:subPropertyOf knora-api:valueHas ; + knora-api:objectType xsd:integer ; + knora-api:subjectType knora-api:DateBase . + knora-api:nextStandoffStartIndex a owl:DatatypeProperty ; rdfs:comment "The next available knora-api:standoffTagHasStartIndex in a sequence of pages of standoff." ; @@ -3231,13 +3231,12 @@ knora-api:GeomValue a owl:Class ; rdfs:subClassOf knora-api:Value ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:geometryValueAsGeometry ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -3246,58 +3245,59 @@ knora-api:GeomValue a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:geometryValueAsGeometry + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:versionArkUrl ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate + ] ; knora-api:isValueClass true . knora-api:Resource a owl:Class ; @@ -3305,19 +3305,19 @@ knora-api:Resource a owl:Class ; rdfs:label "Resource" ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:onProperty knora-api:isDeleted ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -3325,51 +3325,51 @@ knora-api:Resource a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:onProperty knora-api:hasStandoffLinkTo ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:versionDate ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 0 ; owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty knora-api:deletedBy ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; knora-api:isResourceClass true . @@ -3416,63 +3416,62 @@ knora-api:TextRepresentation rdfs:subClassOf knora-api:Representation ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:hasPermissions + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:hasTextFileValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -3482,26 +3481,27 @@ knora-api:TextRepresentation rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasTextFileValue + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:arkUrl + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; knora-api:isResourceClass true . @@ -3561,18 +3561,13 @@ knora-api:StandoffLinkTag rdfs:subClassOf knora-api:StandoffTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -3581,13 +3576,23 @@ knora-api:StandoffLinkTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; @@ -3605,13 +3610,8 @@ knora-api:StandoffLinkTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; knora-api:isStandoffClass true . @@ -3631,13 +3631,12 @@ knora-api:FileValue a owl:Class ; rdfs:subClassOf knora-api:Value ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:fileValueAsUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -3647,17 +3646,17 @@ knora-api:FileValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -3665,42 +3664,43 @@ knora-api:FileValue a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:fileValueAsUrl + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:versionArkUrl + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueHasComment ] ; knora-api:isValueClass true . @@ -3730,72 +3730,72 @@ knora-api:UriValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:uriValueAsUri ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:uriValueAsUri + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:valueAsString + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; knora-api:isValueClass true . @@ -3814,47 +3814,42 @@ knora-api:DecimalValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:decimalValueAsDecimal + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:decimalValueAsDecimal ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -3863,23 +3858,28 @@ knora-api:DecimalValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:arkUrl + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; knora-api:isValueClass true . @@ -3927,113 +3927,113 @@ knora-api:DateValue a owl:Class ; rdfs:subClassOf knora-api:DateBase , knora-api:Value ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasStartMonth + owl:cardinality 1 ; + owl:onProperty knora-api:dateValueHasEndYear ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasCalendar + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartEra + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:dateValueHasStartYear ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndYear + owl:maxCardinality 1 ; + owl:onProperty knora-api:dateValueHasEndDay ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndMonth + owl:cardinality 1 ; + owl:onProperty knora-api:dateValueHasStartEra ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:dateValueHasEndMonth ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndDay + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:dateValueHasCalendar ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartYear + owl:onProperty knora-api:dateValueHasEndEra ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasStartDay + owl:onProperty knora-api:dateValueHasStartMonth ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndEra + owl:maxCardinality 1 ; + owl:onProperty knora-api:dateValueHasStartDay ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; knora-api:isValueClass true . @@ -4059,57 +4059,52 @@ knora-api:IntervalValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:intervalValueHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:onProperty knora-api:intervalValueHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:intervalValueHasEnd + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -4119,17 +4114,22 @@ knora-api:IntervalValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:intervalValueHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:valueCreationDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; knora-api:isValueClass true . @@ -4162,6 +4162,11 @@ knora-api:standoffTagHasEndParent knora-api:objectType knora-api:StandoffTag ; knora-api:subjectType knora-api:StandoffTag . +knora-api:isBuiltIn a owl:DatatypeProperty ; + rdfs:comment "Indicates whether an ontology is built into Knora" ; + rdfs:label "is shared" ; + knora-api:objectType xsd:boolean . + knora-api:hasDDDFileValue a owl:ObjectProperty ; rdfs:comment "Connects a Representation to a 3D-file" ; @@ -4170,13 +4175,8 @@ knora-api:hasDDDFileValue knora-api:isEditable true ; knora-api:isResourceProperty true ; knora-api:objectType knora-api:DDDFileValue ; - knora-api:subjectType knora-api:DDDRepresentation ; - salsah-gui:guiElement salsah-gui:Fileupload . - -knora-api:isBuiltIn a owl:DatatypeProperty ; - rdfs:comment "Indicates whether an ontology is built into Knora" ; - rdfs:label "is shared" ; - knora-api:objectType xsd:boolean . + knora-api:subjectType knora-api:DDDRepresentation ; + salsah-gui:guiElement salsah-gui:Fileupload . knora-api:dateValueHasEndYear a owl:DatatypeProperty ; @@ -4203,72 +4203,72 @@ knora-api:TimeValue a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID + owl:onProperty knora-api:timeValueAsTimeStamp ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:cardinality 1 ; + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:maxCardinality 1 ; + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueAsString ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:valueHasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:timeValueAsTimeStamp + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:cardinality 1 ; + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:userHasPermission ] ; knora-api:isValueClass true . @@ -4298,13 +4298,18 @@ knora-api:LinkObj a owl:Class ; rdfs:subClassOf knora-api:Resource ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -4314,84 +4319,79 @@ knora-api:LinkObj a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:onProperty knora-api:userHasPermission ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty knora-api:hasLinkToValue + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; owl:minCardinality 1 ; - owl:onProperty knora-api:hasLinkTo + owl:onProperty knora-api:hasLinkToValue ] ; rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasComment + owl:minCardinality 1 ; + owl:onProperty knora-api:hasLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:maxCardinality 1 ; + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty knora-api:hasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:hasIncomingLinkValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkToValue + owl:cardinality 1 ; + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:maxCardinality 1 ; + owl:onProperty knora-api:deletedBy ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; @@ -4422,12 +4422,17 @@ knora-api:GeonameValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:valueHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl + owl:maxCardinality 1 ; + owl:onProperty knora-api:valueHasComment + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -4437,84 +4442,79 @@ knora-api:GeonameValue rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueHasUUID - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:geonameValueAsGeonameCode + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:valueCreationDate + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:valueAsString + owl:cardinality 1 ; + owl:onProperty knora-api:valueCreationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted + owl:onProperty knora-api:valueAsString + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:geonameValueAsGeonameCode ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:valueHasComment + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:deletedBy ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl - ] ; knora-api:isValueClass true . knora-api:DateBase a owl:Class ; rdfs:subClassOf knora-api:ValueBase ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndDay + owl:onProperty knora-api:dateValueHasEndMonth ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndEra + owl:onProperty knora-api:dateValueHasEndYear ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasEndYear + owl:onProperty knora-api:dateValueHasEndEra ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartYear + owl:onProperty knora-api:dateValueHasStartEra ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:dateValueHasStartEra + owl:onProperty knora-api:dateValueHasStartYear ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasEndMonth + owl:onProperty knora-api:dateValueHasEndDay ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; @@ -4522,11 +4522,11 @@ knora-api:DateBase a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasStartDay + owl:onProperty knora-api:dateValueHasStartMonth ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; - owl:onProperty knora-api:dateValueHasStartMonth + owl:onProperty knora-api:dateValueHasStartDay ] . knora-api:Region a owl:Class ; @@ -4536,97 +4536,93 @@ knora-api:Region a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:hasPermissions + owl:onProperty rdfs:label ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasStandoffLinkTo + owl:cardinality 1 ; + owl:onProperty knora-api:attachedToUser ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:creationDate + owl:onProperty knora-api:versionArkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:versionDate + owl:cardinality 1 ; + owl:onProperty knora-api:userHasPermission + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:isRegionOfValue ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteComment + owl:onProperty knora-api:lastModificationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:arkUrl + owl:minCardinality 0 ; + owl:onProperty knora-api:hasStandoffLinkTo ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; - owl:onProperty knora-api:isRegionOfValue + owl:onProperty knora-api:hasColor ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deletedBy + owl:onProperty knora-api:isDeleted ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:isDeleted - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty knora-api:hasComment + owl:onProperty knora-api:deletedBy ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:minCardinality 0 ; - owl:onProperty knora-api:hasIncomingLinkValue + owl:maxCardinality 1 ; + owl:onProperty knora-api:versionDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:versionArkUrl - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:minCardinality 1 ; - owl:onProperty knora-api:hasGeometry - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:hasColor - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:cardinality 1 ; - owl:onProperty knora-api:isRegionOf + owl:onProperty knora-api:attachedToProject ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty rdfs:label + owl:onProperty knora-api:arkUrl ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:attachedToUser + owl:onProperty knora-api:creationDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:attachedToProject + owl:maxCardinality 1 ; + owl:onProperty knora-api:deleteDate ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:lastModificationDate + owl:cardinality 1 ; + owl:onProperty knora-api:hasPermissions ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:deleteDate + owl:onProperty knora-api:deleteComment + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:cardinality 1 ; + owl:onProperty knora-api:isRegionOf + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 1 ; + owl:onProperty knora-api:hasComment ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -4635,8 +4631,12 @@ knora-api:Region a owl:Class ; ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:userHasPermission + owl:minCardinality 0 ; + owl:onProperty knora-api:hasIncomingLinkValue + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 1 ; + owl:onProperty knora-api:hasGeometry ] ; knora-api:canBeInstantiated true ; knora-api:isResourceClass true ; diff --git a/test_data/ontologyR2RV2/standoffOntologyWithValueObjects.ttl b/test_data/ontologyR2RV2/standoffOntologyWithValueObjects.ttl index 13c8ab87d9..25b95cfa41 100644 --- a/test_data/ontologyR2RV2/standoffOntologyWithValueObjects.ttl +++ b/test_data/ontologyR2RV2/standoffOntologyWithValueObjects.ttl @@ -10,11 +10,6 @@ standoff:StandoffParagraphTag a owl:Class ; rdfs:comment "Represents a paragraph in a TextValue" ; rdfs:subClassOf standoff:StandoffStructuralTag ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; @@ -23,17 +18,17 @@ standoff:StandoffParagraphTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -42,23 +37,28 @@ standoff:StandoffParagraphTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasUUID ] ; knora-api:isStandoffClass true . @@ -66,20 +66,15 @@ standoff:StandoffTableCellTag a owl:Class ; rdfs:comment "Represents a cell in a table" ; rdfs:subClassOf standoff:StandoffStructuralTag ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -89,12 +84,12 @@ standoff:StandoffTableCellTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -103,18 +98,23 @@ standoff:StandoffTableCellTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndIndex + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; knora-api:isStandoffClass true . @@ -122,11 +122,6 @@ standoff:StandoffBlockquoteTag a owl:Class ; rdfs:comment "Represents a section that is quoted from another source in a text" ; rdfs:subClassOf standoff:StandoffStructuralTag ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; @@ -135,38 +130,43 @@ standoff:StandoffBlockquoteTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:standoffTagHasEndParent ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; @@ -180,22 +180,18 @@ standoff:StandoffRootTag rdfs:subClassOf knora-api:StandoffTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID - ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty standoff:standoffRootTagHasDocumentType + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -205,17 +201,21 @@ standoff:StandoffRootTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndIndex + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty standoff:standoffRootTagHasDocumentType ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -225,7 +225,7 @@ standoff:StandoffRootTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -241,7 +241,7 @@ standoff:StandoffCiteTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -251,7 +251,7 @@ standoff:StandoffCiteTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -260,18 +260,18 @@ standoff:StandoffCiteTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -280,13 +280,13 @@ standoff:StandoffCiteTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; knora-api:isStandoffClass true . @@ -297,52 +297,52 @@ standoff:StandoffHeader4Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasUUID ] ; knora-api:isStandoffClass true . @@ -359,52 +359,52 @@ standoff:StandoffVisualTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; knora-api:isStandoffClass true . @@ -414,18 +414,18 @@ standoff:StandoffSubscriptTag rdfs:subClassOf standoff:StandoffVisualTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -434,33 +434,33 @@ standoff:StandoffSubscriptTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; knora-api:isStandoffClass true . @@ -470,27 +470,23 @@ standoff:StandoffHyperlinkTag rdfs:subClassOf knora-api:StandoffUriTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; - rdfs:subClassOf [ a owl:Restriction ; - owl:maxCardinality 1 ; - owl:onProperty standoff:standoffHyperlinkTagHasTarget - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -500,17 +496,21 @@ standoff:StandoffHyperlinkTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:uriValueAsUri + ] ; + rdfs:subClassOf [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty standoff:standoffHyperlinkTagHasTarget ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -519,13 +519,13 @@ standoff:StandoffHyperlinkTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:uriValueAsUri + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndParent ] ; knora-api:isStandoffClass true . @@ -535,38 +535,38 @@ standoff:StandoffTableRowTag rdfs:subClassOf standoff:StandoffStructuralTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -580,8 +580,8 @@ standoff:StandoffTableRowTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; knora-api:isStandoffClass true . @@ -591,23 +591,23 @@ standoff:StandoffHeader3Tag rdfs:subClassOf standoff:StandoffStructuralTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -616,13 +616,13 @@ standoff:StandoffHeader3Tag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -631,13 +631,13 @@ standoff:StandoffHeader3Tag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasStartParent ] ; knora-api:isStandoffClass true . @@ -647,13 +647,18 @@ standoff:StandoffBoldTag rdfs:subClassOf standoff:StandoffVisualTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -663,37 +668,32 @@ standoff:StandoffBoldTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; knora-api:isStandoffClass true . @@ -704,12 +704,17 @@ standoff:StandoffLineTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParent + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -718,8 +723,8 @@ standoff:StandoffLineTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -734,7 +739,7 @@ standoff:StandoffLineTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -743,13 +748,8 @@ standoff:StandoffLineTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; knora-api:isStandoffClass true . @@ -759,13 +759,18 @@ standoff:StandoffBrTag rdfs:subClassOf standoff:StandoffStructuralTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndIndex + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -775,7 +780,7 @@ standoff:StandoffBrTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -784,28 +789,23 @@ standoff:StandoffBrTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParent ] ; knora-api:isStandoffClass true . @@ -816,7 +816,7 @@ standoff:StandoffPreTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -825,18 +825,23 @@ standoff:StandoffPreTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -846,22 +851,17 @@ standoff:StandoffPreTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:standoffTagHasStart ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasEnd ] ; knora-api:isStandoffClass true . @@ -872,52 +872,52 @@ standoff:StandoffTableTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasUUID ] ; knora-api:isStandoffClass true . @@ -928,22 +928,22 @@ standoff:StandoffHeader2Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -958,22 +958,22 @@ standoff:StandoffHeader2Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; knora-api:isStandoffClass true . @@ -983,34 +983,39 @@ standoff:StandoffSuperscriptTag rdfs:subClassOf standoff:StandoffVisualTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; owl:onProperty knora-api:standoffTagHasUUID ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; @@ -1019,18 +1024,13 @@ standoff:StandoffSuperscriptTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:standoffTagHasEndIndex ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd - ] ; knora-api:isStandoffClass true . standoff:StandoffItalicTag @@ -1040,52 +1040,52 @@ standoff:StandoffItalicTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; knora-api:isStandoffClass true . @@ -1093,11 +1093,6 @@ standoff:StandoffTableBodyTag a owl:Class ; rdfs:comment "Represents a table body in a TextValue" ; rdfs:subClassOf standoff:StandoffStructuralTag ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent - ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; @@ -1106,22 +1101,17 @@ standoff:StandoffTableBodyTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1136,12 +1126,22 @@ standoff:StandoffTableBodyTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasUUID + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndIndex + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; knora-api:isStandoffClass true . @@ -1152,12 +1152,17 @@ standoff:StandoffHeader1Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasStart + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1166,18 +1171,18 @@ standoff:StandoffHeader1Tag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1186,19 +1191,14 @@ standoff:StandoffHeader1Tag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd - ] ; knora-api:isStandoffClass true . standoff:StandoffStructuralTag @@ -1207,8 +1207,8 @@ standoff:StandoffStructuralTag rdfs:subClassOf knora-api:StandoffTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1217,13 +1217,18 @@ standoff:StandoffStructuralTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndIndex + ] ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1233,27 +1238,22 @@ standoff:StandoffStructuralTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasStart ] ; knora-api:isStandoffClass true . @@ -1263,53 +1263,53 @@ standoff:StandoffListElementTag rdfs:subClassOf standoff:StandoffStructuralTag ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; knora-api:isStandoffClass true . @@ -1320,32 +1320,32 @@ standoff:StandoffStrikethroughTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1354,13 +1354,13 @@ standoff:StandoffStrikethroughTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1373,30 +1373,35 @@ standoff:StandoffUnorderedListTag a owl:Class ; rdfs:comment "Represents an unordered list in a TextValue" ; rdfs:subClassOf standoff:StandoffStructuralTag ; + rdfs:subClassOf [ a owl:Restriction ; + knora-api:isInherited true ; + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex + ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1411,17 +1416,12 @@ standoff:StandoffUnorderedListTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex - ] ; - rdfs:subClassOf [ a owl:Restriction ; - knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEnd ] ; knora-api:isStandoffClass true . @@ -1432,7 +1432,7 @@ standoff:StandoffHeader6Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1442,22 +1442,22 @@ standoff:StandoffHeader6Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1467,7 +1467,7 @@ standoff:StandoffHeader6Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1476,8 +1476,8 @@ standoff:StandoffHeader6Tag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; knora-api:isStandoffClass true . @@ -1488,37 +1488,37 @@ standoff:StandoffUnderlineTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1527,13 +1527,13 @@ standoff:StandoffUnderlineTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; knora-api:isStandoffClass true . @@ -1544,52 +1544,52 @@ standoff:StandoffCodeTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; knora-api:isStandoffClass true . @@ -1600,22 +1600,22 @@ standoff:StandoffOrderedListTag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParent + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1624,28 +1624,28 @@ standoff:StandoffOrderedListTag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasEnd ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasEnd + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; knora-api:isStandoffClass true . @@ -1662,12 +1662,12 @@ standoff:StandoffHeader5Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasUUID + owl:onProperty knora-api:standoffTagHasStartIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParent + owl:onProperty knora-api:standoffTagHasEndParentIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1676,13 +1676,13 @@ standoff:StandoffHeader5Tag ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartIndex + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasOriginalXMLID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasStartParentIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasStart ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; @@ -1692,22 +1692,22 @@ standoff:StandoffHeader5Tag rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasOriginalXMLID + owl:onProperty knora-api:standoffTagHasStartParent ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:cardinality 1 ; - owl:onProperty knora-api:standoffTagHasStart + owl:maxCardinality 1 ; + owl:onProperty knora-api:standoffTagHasEndIndex ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; - owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndIndex + owl:cardinality 1 ; + owl:onProperty knora-api:standoffTagHasUUID ] ; rdfs:subClassOf [ a owl:Restriction ; knora-api:isInherited true ; owl:maxCardinality 1 ; - owl:onProperty knora-api:standoffTagHasEndParentIndex + owl:onProperty knora-api:standoffTagHasStartParentIndex ] ; knora-api:isStandoffClass true . diff --git a/third_party/dependencies.bzl b/third_party/dependencies.bzl index 76b9392c0c..f7514a1693 100644 --- a/third_party/dependencies.bzl +++ b/third_party/dependencies.bzl @@ -27,6 +27,12 @@ def dependencies(): "com.typesafe.akka:akka-http-jackson_2.13:%s" % (AKKA_HTTP_VERSION), "com.typesafe:config:1.3.3", + # ZIO + "dev.zio:zio_2.13:1.0.9", + "dev.zio:zio-json_2.13:0.1.5", + "dev.zio:zio-test_2.13:1.0.9", + "dev.zio:zio-test-junit_2.13:1.0.9", + # CORS support "ch.megard:akka-http-cors_2.13:1.0.0", @@ -125,7 +131,7 @@ def dependencies(): "org.scalatest:scalatest-shouldmatchers_2.13:3.2.2", "org.scalatest:scalatest-compatible:3.2.2", "org.testcontainers:testcontainers:1.15.3", - "junit:junit:4.13", + "junit:junit:4.13.2", "io.gatling.highcharts:gatling-charts-highcharts:3.2.1", "io.gatling:gatling-test-framework:3.2.1", diff --git a/webapi/BUILD.bazel b/webapi/BUILD.bazel index eded0335cd..8524cda663 100644 --- a/webapi/BUILD.bazel +++ b/webapi/BUILD.bazel @@ -179,6 +179,7 @@ scala_library( "@maven//:com_typesafe_akka_akka_http_spray_json_2_13", "@maven//:com_typesafe_akka_akka_stream_2_13", "@maven//:com_typesafe_config", + "@maven//:dev_zio_zio_2_13", "@maven//:io_spray_spray_json_2_13", "@maven//:org_scalactic_scalactic_2_13", "@maven//:org_scalatest_scalatest_core_2_13", diff --git a/webapi/scripts/fuseki-init-knora-test.sh b/webapi/scripts/fuseki-init-knora-test.sh index 184f9e865e..c75e15aeda 100755 --- a/webapi/scripts/fuseki-init-knora-test.sh +++ b/webapi/scripts/fuseki-init-knora-test.sh @@ -33,3 +33,5 @@ upload-graph ../../test_data/all_data/biblio-data.ttl http://www.knora.org/data/ upload-graph ../../test_data/all_data/beol-data.ttl http://www.knora.org/data/0801/beol upload-graph ../../test_data/ontologies/webern-onto.ttl http://www.knora.org/ontology/0806/webern upload-graph ../../test_data/all_data/webern-data.ttl http://www.knora.org/data/0806/webern +upload-graph ../../test_data/ontologies/freetest-onto.ttl http://www.knora.org/ontology/0001/freetest +upload-graph ../../test_data/all_data/freetest-data.ttl http://www.knora.org/data/0001/freetest diff --git a/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala b/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala index 1f90bee9f6..7840900dcc 100644 --- a/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala +++ b/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala @@ -321,10 +321,10 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire case GetAppState() => logger.debug("ApplicationStateActor - GetAppState - value: {}", appState) - sender ! appState + sender() ! appState case ActorReady() => - sender ! ActorReadyAck() + sender() ! ActorReadyAck() case SetAllowReloadOverHTTPState(value) => logger.debug("ApplicationStateActor - SetAllowReloadOverHTTPState - value: {}", value) @@ -332,7 +332,7 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire case GetAllowReloadOverHTTPState() => logger.debug("ApplicationStateActor - GetAllowReloadOverHTTPState - value: {}", allowReloadOverHTTPState) - sender ! (allowReloadOverHTTPState | knoraSettings.allowReloadOverHTTP) + sender() ! (allowReloadOverHTTPState | knoraSettings.allowReloadOverHTTP) case SetPrintConfigExtendedState(value) => logger.debug("ApplicationStateActor - SetPrintConfigExtendedState - value: {}", value) @@ -340,7 +340,7 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire case GetPrintConfigExtendedState() => logger.debug("ApplicationStateActor - GetPrintConfigExtendedState - value: {}", printConfigState) - sender ! (printConfigState | knoraSettings.printExtendedConfig) + sender() ! (printConfigState | knoraSettings.printExtendedConfig) /* check repository request */ case CheckTriplestore() => @@ -481,11 +481,8 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire def appStart(ignoreRepository: Boolean, requiresIIIFService: Boolean, retryCnt: Int): Unit = { val bindingFuture: Future[Http.ServerBinding] = Http() - .bindAndHandle( - Route.handlerFlow(apiRoutes), - knoraSettings.internalKnoraApiHost, - knoraSettings.internalKnoraApiPort - ) + .newServerAt(knoraSettings.internalKnoraApiHost, knoraSettings.internalKnoraApiPort) + .bindFlow(Route.toFlow(apiRoutes)) bindingFuture onComplete { case Success(_) => diff --git a/webapi/src/main/scala/org/knora/webapi/app/BUILD.bazel b/webapi/src/main/scala/org/knora/webapi/app/BUILD.bazel index 89a352495c..0fec62f73e 100644 --- a/webapi/src/main/scala/org/knora/webapi/app/BUILD.bazel +++ b/webapi/src/main/scala/org/knora/webapi/app/BUILD.bazel @@ -30,7 +30,6 @@ scala_library( "@maven//:com_typesafe_akka_akka_http_2_13", "@maven//:com_typesafe_akka_akka_http_core_2_13", "@maven//:com_typesafe_akka_akka_http_spray_json_2_13", - "@maven//:com_typesafe_akka_akka_parsing_2_13", "@maven//:com_typesafe_akka_akka_stream_2_13", "@maven//:com_typesafe_config", "@maven//:com_typesafe_scala_logging_scala_logging_2_13", diff --git a/webapi/src/main/scala/org/knora/webapi/contributors/GenerateContributorsFile.scala b/webapi/src/main/scala/org/knora/webapi/contributors/GenerateContributorsFile.scala index ec6cfdafa3..f4a3d452fb 100644 --- a/webapi/src/main/scala/org/knora/webapi/contributors/GenerateContributorsFile.scala +++ b/webapi/src/main/scala/org/knora/webapi/contributors/GenerateContributorsFile.scala @@ -31,19 +31,19 @@ import spray.json.{JsArray, JsNull, JsNumber, JsObject, JsString, JsValue, JsonP import scala.io.Source /** - * Generates the file Contributors.md, using the GitHub API. - */ + * Generates the file Contributors.md, using the GitHub API. + */ object GenerateContributorsFile extends App { // Configuration - val contributorsUrl = "https://api.github.com/repos/dasch-swiss/knora-api/contributors" + val contributorsUrl = "https://api.github.com/repos/dasch-swiss/knora-api/contributors" val defaultOutputFile = "Contributors.md" // Command-line args - private val conf = new GenerateContributorsFileConf(args) - private val token = conf.token.toOption + private val conf = new GenerateContributorsFileConf(args.toIndexedSeq) + private val token = conf.token.toOption private val outputFile: Path = Paths.get(conf.output()) // Get the list of contributors. @@ -89,11 +89,11 @@ object GenerateContributorsFile extends App { FileUtil.writeTextFile(file = outputFile, content = contributorsText) /** - * Makes an HTTP GET connection to the GitHub API. - * - * @param url a GitHub API URL. - * @return the response, parsed as JSON. - */ + * Makes an HTTP GET connection to the GitHub API. + * + * @param url a GitHub API URL. + * @return the response, parsed as JSON. + */ private def getFromGitHubApi(url: String): JsValue = { val connection: URLConnection = new URL(url).openConnection @@ -112,13 +112,13 @@ object GenerateContributorsFile extends App { } /** - * Parses command-line arguments. - */ + * Parses command-line arguments. + */ private class GenerateContributorsFileConf(arguments: Seq[String]) extends ScallopConf(arguments) { banner(s""" - |Generates a file listing the contributors to Knora. - | - |Usage: org.knora.webapi.util.GenerateContributorsFile [ -t TOKEN ] [ -o OUTPUT ] + |Generates a file listing the contributors to Knora. + | + |Usage: org.knora.webapi.util.GenerateContributorsFile [ -t TOKEN ] [ -o OUTPUT ] """.stripMargin) val token: ScallopOption[String] = opt[String](descr = "GitHub API token") diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraResponseV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraResponseV2.scala index 1a47063c81..b7f79eea95 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraResponseV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraResponseV2.scala @@ -17,48 +17,60 @@ * License along with Knora. If not, see . */ -package org.knora.webapi -package messages.v2.responder - -import exceptions.{AssertionException, BadRequestException} -import feature.FeatureFactoryConfig -import messages.OntologyConstants -import messages.admin.responder.projectsmessages.ProjectADM -import messages.util.rdf._ -import settings.KnoraSettingsImpl +package org.knora.webapi.messages.v2.responder + +import org.knora.webapi.{ + ApiV2Complex, + ApiV2Schema, + ApiV2Simple, + InternalSchema, + OntologySchema, + SchemaOption, + SchemaOptions +} +import org.knora.webapi.exceptions.{AssertionException, BadRequestException} +import org.knora.webapi.feature.FeatureFactoryConfig +import org.knora.webapi.messages.OntologyConstants +import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM +import org.knora.webapi.messages.util.rdf._ +import org.knora.webapi.settings.KnoraSettingsImpl /** - * A trait for Knora API V2 response messages. - */ + * A trait for Knora API V2 response messages. + */ trait KnoraResponseV2 { /** - * Returns this response message in the requested format. - * - * @param rdfFormat the RDF format selected for the response. - * @param targetSchema the response schema. - * @param schemaOptions the schema options. - * @param settings the application settings. - * @param featureFactoryConfig the feature factory configuration. - * @return a formatted string representing this response message. - */ - def format(rdfFormat: RdfFormat, - targetSchema: OntologySchema, - schemaOptions: Set[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl): String + * Returns this response message in the requested format. + * + * @param rdfFormat the RDF format selected for the response. + * @param targetSchema the response schema. + * @param schemaOptions the schema options. + * @param settings the application settings. + * @param featureFactoryConfig the feature factory configuration. + * @return a formatted string representing this response message. + */ + def format( + rdfFormat: RdfFormat, + targetSchema: OntologySchema, + schemaOptions: Set[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl + ): String } /** - * A trait for Knora API V2 response messages that are constructed as JSON-LD documents. - */ + * A trait for Knora API V2 response messages that are constructed as JSON-LD documents. + */ trait KnoraJsonLDResponseV2 extends KnoraResponseV2 { - override def format(rdfFormat: RdfFormat, - targetSchema: OntologySchema, - schemaOptions: Set[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl): String = { + override def format( + rdfFormat: RdfFormat, + targetSchema: OntologySchema, + schemaOptions: Set[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl + ): String = { val targetApiV2Schema = targetSchema match { case apiV2Schema: ApiV2Schema => apiV2Schema case InternalSchema => throw AssertionException(s"Response cannot be returned in the internal schema") @@ -80,7 +92,7 @@ trait KnoraJsonLDResponseV2 extends KnoraResponseV2 { case nonJsonLD: NonJsonLD => // Some other format. Convert the JSON-LD document to an RDF model. val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) - val rdfModel: RdfModel = jsonLDDocument.toRdfModel(rdfFormatUtil.getRdfModelFactory) + val rdfModel: RdfModel = jsonLDDocument.toRdfModel(rdfFormatUtil.getRdfModelFactory) // Convert the model to the requested format. rdfFormatUtil.format( @@ -92,32 +104,36 @@ trait KnoraJsonLDResponseV2 extends KnoraResponseV2 { } /** - * Converts the response to a data structure that can be used to generate JSON-LD. - * - * @param targetSchema the Knora API schema to be used in the JSON-LD document. - * @return a [[JsonLDDocument]] representing the response. - */ - protected def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument + * Converts the response to a data structure that can be used to generate JSON-LD. + * + * @param targetSchema the Knora API schema to be used in the JSON-LD document. + * @return a [[JsonLDDocument]] representing the response. + */ + protected def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument } /** - * A trait for Knora API V2 response messages that are constructed as - * strings in Turtle format in the internal schema. - */ + * A trait for Knora API V2 response messages that are constructed as + * strings in Turtle format in the internal schema. + */ trait KnoraTurtleResponseV2 extends KnoraResponseV2 { /** - * A string containing RDF data in Turtle format. - */ + * A string containing RDF data in Turtle format. + */ protected val turtle: String - override def format(rdfFormat: RdfFormat, - targetSchema: OntologySchema, - schemaOptions: Set[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl): String = { + override def format( + rdfFormat: RdfFormat, + targetSchema: OntologySchema, + schemaOptions: Set[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl + ): String = { if (targetSchema != InternalSchema) { throw AssertionException(s"Response can be returned only in the internal schema") } @@ -131,7 +147,7 @@ trait KnoraTurtleResponseV2 extends KnoraResponseV2 { case _ => // Some other format. Parse the Turtle to an RdfModel. val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) - val rdfModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtle, rdfFormat = Turtle) + val rdfModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtle, rdfFormat = Turtle) // Return the model in the requested format. rdfFormatUtil.format( @@ -144,14 +160,16 @@ trait KnoraTurtleResponseV2 extends KnoraResponseV2 { } /** - * Provides a message indicating that the result of an operation was successful. - * - * @param message the message to be returned. - */ + * Provides a message indicating that the result of an operation was successful. + * + * @param message the message to be returned. + */ case class SuccessResponseV2(message: String) extends KnoraJsonLDResponseV2 { - def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { val (ontologyPrefixExpansion, resultProp) = targetSchema match { case ApiV2Simple => (OntologyConstants.KnoraApiV2Simple.KnoraApiV2PrefixExpansion, OntologyConstants.KnoraApiV2Simple.Result) @@ -171,14 +189,16 @@ case class SuccessResponseV2(message: String) extends KnoraJsonLDResponseV2 { } /** - * Indicates whether an operation can be performed. - * - * @param canDo `true` if the operation can be performed. - */ -case class CanDoResponseV2(canDo: Boolean) extends KnoraJsonLDResponseV2 { - def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + * Indicates whether an operation can be performed. + * + * @param canDo `true` if the operation can be performed. + */ +final case class CanDoResponseV2(canDo: Boolean) extends KnoraJsonLDResponseV2 { + def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { if (targetSchema != ApiV2Complex) { throw BadRequestException(s"Response is available only in the complex schema") } @@ -190,39 +210,41 @@ case class CanDoResponseV2(canDo: Boolean) extends KnoraJsonLDResponseV2 { context = JsonLDObject( Map( OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> JsonLDString( - OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion)) + OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion + ) + ) ) ) } } /** - * A trait for content classes that can convert themselves between internal and internal schemas. - * - * @tparam C the type of the content class that extends this trait. - */ + * A trait for content classes that can convert themselves between internal and internal schemas. + * + * @tparam C the type of the content class that extends this trait. + */ trait KnoraContentV2[C <: KnoraContentV2[C]] { this: C => def toOntologySchema(targetSchema: OntologySchema): C } /** - * A trait for read wrappers that can convert themselves to external schemas. - * - * @tparam C the type of the read wrapper that extends this trait. - */ + * A trait for read wrappers that can convert themselves to external schemas. + * + * @tparam C the type of the read wrapper that extends this trait. + */ trait KnoraReadV2[C <: KnoraReadV2[C]] { this: C => def toOntologySchema(targetSchema: ApiV2Schema): C } /** - * Allows the successful result of an update operation to indicate which project was updated. - */ + * Allows the successful result of an update operation to indicate which project was updated. + */ trait UpdateResultInProject { /** - * The project that was updated. - */ + * The project that was updated. + */ def projectADM: ProjectADM } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala index 8d4a4f77b4..4de1e3b9b6 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala @@ -47,72 +47,74 @@ import java.util.UUID import scala.concurrent.{ExecutionContext, Future} /** - * An abstract trait for messages that can be sent to `ResourcesResponderV2`. - */ + * An abstract trait for messages that can be sent to `ResourcesResponderV2`. + */ sealed trait OntologiesResponderRequestV2 extends KnoraRequestV2 { def requestingUser: UserADM } /** - * Requests that all ontologies in the repository are loaded. This message must be sent only once, when the application - * starts, before it accepts any API requests. A successful response will be a [[SuccessResponseV2]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Requests that all ontologies in the repository are loaded. This message must be sent only once, when the application + * starts, before it accepts any API requests. A successful response will be a [[SuccessResponseV2]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ case class LoadOntologiesRequestV2(featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Requests the creation of an empty ontology. A successful response will be a [[ReadOntologyV2]]. - * - * @param ontologyName the name of the ontology to be created. - * @param projectIri the IRI of the project that the ontology will belong to. - * @param isShared the flag that shows if an ontology is a shared one. - * @param label the label of the ontology. - * @param comment the optional comment that described the ontology to be created. - * @param apiRequestID the ID of the API request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class CreateOntologyRequestV2(ontologyName: String, - projectIri: SmartIri, - isShared: Boolean = false, - label: String, - comment: Option[String] = None, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 - -/** - * Constructs instances of [[CreateOntologyRequestV2]] based on JSON-LD requests. - */ + * Requests the creation of an empty ontology. A successful response will be a [[ReadOntologyV2]]. + * + * @param ontologyName the name of the ontology to be created. + * @param projectIri the IRI of the project that the ontology will belong to. + * @param isShared the flag that shows if an ontology is a shared one. + * @param label the label of the ontology. + * @param comment the optional comment that described the ontology to be created. + * @param apiRequestID the ID of the API request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class CreateOntologyRequestV2( + ontologyName: String, + projectIri: SmartIri, + isShared: Boolean = false, + label: String, + comment: Option[String] = None, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 + +/** + * Constructs instances of [[CreateOntologyRequestV2]] based on JSON-LD requests. + */ object CreateOntologyRequestV2 extends KnoraJsonLDRequestReaderV2[CreateOntologyRequestV2] { /** - * Converts JSON-LD input into a [[CreateOntologyRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[CreateOntologyRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[CreateOntologyRequestV2] = { + * Converts JSON-LD input into a [[CreateOntologyRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[CreateOntologyRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[CreateOntologyRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -121,23 +123,27 @@ object CreateOntologyRequestV2 extends KnoraJsonLDRequestReaderV2[CreateOntology requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): CreateOntologyRequestV2 = { + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): CreateOntologyRequestV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val ontologyName: String = jsonLDDocument.requireStringWithValidation( OntologyConstants.KnoraApiV2Complex.OntologyName, - stringFormatter.validateProjectSpecificOntologyName) + stringFormatter.validateProjectSpecificOntologyName + ) val label: String = jsonLDDocument.requireStringWithValidation(OntologyConstants.Rdfs.Label, stringFormatter.toSparqlEncodedString) val comment: Option[String] = jsonLDDocument.maybeStringWithValidation(OntologyConstants.Rdfs.Comment, stringFormatter.toSparqlEncodedString) - val projectIri: SmartIri = jsonLDDocument.requireIriInObject(OntologyConstants.KnoraApiV2Complex.AttachedToProject, - stringFormatter.toSmartIriWithErr) + val projectIri: SmartIri = jsonLDDocument.requireIriInObject( + OntologyConstants.KnoraApiV2Complex.AttachedToProject, + stringFormatter.toSmartIriWithErr + ) val isShared: Boolean = jsonLDDocument.maybeBoolean(OntologyConstants.KnoraApiV2Complex.IsShared).exists(identity) CreateOntologyRequestV2( @@ -154,73 +160,76 @@ object CreateOntologyRequestV2 extends KnoraJsonLDRequestReaderV2[CreateOntology } /** - * Checks whether an ontology can be deleted. A successful response will be a [[CanDoResponseV2]]. - * - * @param ontologyIri the ontology IRI. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class CanDeleteOntologyRequestV2(ontologyIri: SmartIri, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Checks whether an ontology can be deleted. A successful response will be a [[CanDoResponseV2]]. + * + * @param ontologyIri the ontology IRI. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class CanDeleteOntologyRequestV2( + ontologyIri: SmartIri, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Requests that an ontology is deleted. All the entities in the ontology must be unused in data. - * - * @param ontologyIri the IRI of the ontology to delete. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class DeleteOntologyRequestV2(ontologyIri: SmartIri, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests that an ontology is deleted. All the entities in the ontology must be unused in data. + * + * @param ontologyIri the IRI of the ontology to delete. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class DeleteOntologyRequestV2( + ontologyIri: SmartIri, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Represents information taken from an [[InputOntologyV2]], representing a request to update a property - * definition. - * - * @param propertyInfoContent information to be updated in the property definition. - * @param lastModificationDate the ontology's last modification date. - */ + * Represents information taken from an [[InputOntologyV2]], representing a request to update a property + * definition. + * + * @param propertyInfoContent information to be updated in the property definition. + * @param lastModificationDate the ontology's last modification date. + */ case class PropertyUpdateInfo(propertyInfoContent: PropertyInfoContentV2, lastModificationDate: Instant) /** - * Represents information taken from an [[InputOntologyV2]], representing a request to update a class - * definition. - * - * @param classInfoContent information to be updated in the class definition. - * @param lastModificationDate the ontology's last modification date. - */ + * Represents information taken from an [[InputOntologyV2]], representing a request to update a class + * definition. + * + * @param classInfoContent information to be updated in the class definition. + * @param lastModificationDate the ontology's last modification date. + */ case class ClassUpdateInfo(classInfoContent: ClassInfoContentV2, lastModificationDate: Instant) /** - * Assists in the processing of JSON-LD in ontology entity update requests. - */ + * Assists in the processing of JSON-LD in ontology entity update requests. + */ object OntologyUpdateHelper { /** - * Gets the ontology's last modification date from the request. - * - * @param inputOntologyV2 an [[InputOntologyV2]] representing the ontology to be updated. - * @return the ontology's last modification date. - */ - def getOntologyLastModificationDate(inputOntologyV2: InputOntologyV2): Instant = { + * Gets the ontology's last modification date from the request. + * + * @param inputOntologyV2 an [[InputOntologyV2]] representing the ontology to be updated. + * @return the ontology's last modification date. + */ + def getOntologyLastModificationDate(inputOntologyV2: InputOntologyV2): Instant = inputOntologyV2.ontologyMetadata.lastModificationDate.getOrElse( throw BadRequestException( - s"An ontology update request must include the ontology's knora-api:lastModificationDate")) - } + s"An ontology update request must include the ontology's knora-api:lastModificationDate" + ) + ) /** - * Gets a class definition from the request. - * - * @param inputOntologyV2 an [[InputOntologyV2]] that must contain a single class definition. - * @return a [[ClassUpdateInfo]] containing the class definition and the ontology's last modification date. - */ + * Gets a class definition from the request. + * + * @param inputOntologyV2 an [[InputOntologyV2]] that must contain a single class definition. + * @return a [[ClassUpdateInfo]] containing the class definition and the ontology's last modification date. + */ def getClassDef(inputOntologyV2: InputOntologyV2): ClassUpdateInfo = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -234,7 +243,8 @@ object OntologyUpdateHelper { if (inputOntologyV2.properties.nonEmpty || inputOntologyV2.individuals.nonEmpty) { throw BadRequestException( - s"A property or individual definition cannot be submitted when creating or modifying a class") + s"A property or individual definition cannot be submitted when creating or modifying a class" + ) } if (inputOntologyV2.classes.size != 1) { @@ -247,9 +257,11 @@ object OntologyUpdateHelper { val classIri = classInfoContent.classIri - if (!(classIri.isKnoraApiV2EntityIri && - classIri.getOntologySchema.contains(ApiV2Complex) && - classIri.getOntologyFromEntity == externalOntologyIri)) { + if ( + !(classIri.isKnoraApiV2EntityIri && + classIri.getOntologySchema.contains(ApiV2Complex) && + classIri.getOntologyFromEntity == externalOntologyIri) + ) { throw BadRequestException(s"Invalid class IRI: $classIri") } @@ -276,11 +288,11 @@ object OntologyUpdateHelper { } /** - * Gets a property definition from the request. - * - * @param inputOntologyV2 an [[InputOntologyV2]] that must contain a single property definition. - * @return a [[PropertyUpdateInfo]] containing the property definition and the ontology's last modification date. - */ + * Gets a property definition from the request. + * + * @param inputOntologyV2 an [[InputOntologyV2]] that must contain a single property definition. + * @return a [[PropertyUpdateInfo]] containing the property definition and the ontology's last modification date. + */ def getPropertyDef(inputOntologyV2: InputOntologyV2): PropertyUpdateInfo = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -294,7 +306,8 @@ object OntologyUpdateHelper { if (inputOntologyV2.classes.nonEmpty || inputOntologyV2.individuals.nonEmpty) { throw BadRequestException( - s"A class or individual definition cannot be submitted when creating or modifying a property") + s"A class or individual definition cannot be submitted when creating or modifying a property" + ) } if (inputOntologyV2.properties.size != 1) { @@ -307,9 +320,11 @@ object OntologyUpdateHelper { val propertyIri = propertyInfoContent.propertyIri - if (!(propertyIri.isKnoraApiV2EntityIri && - propertyIri.getOntologySchema.contains(ApiV2Complex) && - propertyIri.getOntologyFromEntity == externalOntologyIri)) { + if ( + !(propertyIri.isKnoraApiV2EntityIri && + propertyIri.getOntologySchema.contains(ApiV2Complex) && + propertyIri.getOntologyFromEntity == externalOntologyIri) + ) { throw BadRequestException(s"Invalid property IRI: $propertyIri") } @@ -341,11 +356,11 @@ object OntologyUpdateHelper { ) /** - * Gets the values of `rdfs:label` or `rdfs:comment` from a request to update them. - * - * @param entityInfoContent the data submitted about the entity to be updated. - * @return the values of that predicate. - */ + * Gets the values of `rdfs:label` or `rdfs:comment` from a request to update them. + * + * @param entityInfoContent the data submitted about the entity to be updated. + * @return the values of that predicate. + */ def getLabelsOrComments(entityInfoContent: EntityInfoContentV2): PredicateInfoV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -356,16 +371,18 @@ object OntologyUpdateHelper { } val predicateInfoToUpdate = predicatesWithNewData.values.head - val predicateToUpdate = predicateInfoToUpdate.predicateIri + val predicateToUpdate = predicateInfoToUpdate.predicateIri if (!LabelAndCommentPredicates.contains(predicateToUpdate.toString)) { throw BadRequestException(s"Invalid predicate: $predicateToUpdate") } - if (!predicateInfoToUpdate.objects.forall { - case StringLiteralV2(_, Some(_)) => true - case _ => false - }) { + if ( + !predicateInfoToUpdate.objects.forall { + case StringLiteralV2(_, Some(_)) => true + case _ => false + } + ) { throw BadRequestException(s"Missing language code in rdfs:label or rdfs:comment") } @@ -374,47 +391,49 @@ object OntologyUpdateHelper { } /** - * Requests the addition of a property to an ontology. A successful response will be a [[ReadOntologyV2]]. - * - * @param propertyInfoContent an [[PropertyInfoContentV2]] containing the property definition. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class CreatePropertyRequestV2(propertyInfoContent: PropertyInfoContentV2, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests the addition of a property to an ontology. A successful response will be a [[ReadOntologyV2]]. + * + * @param propertyInfoContent an [[PropertyInfoContentV2]] containing the property definition. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class CreatePropertyRequestV2( + propertyInfoContent: PropertyInfoContentV2, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Constructs instances of [[CreatePropertyRequestV2]] based on JSON-LD requests. - */ + * Constructs instances of [[CreatePropertyRequestV2]] based on JSON-LD requests. + */ object CreatePropertyRequestV2 extends KnoraJsonLDRequestReaderV2[CreatePropertyRequestV2] { /** - * Converts a JSON-LD request to a [[CreatePropertyRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[CreatePropertyRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[CreatePropertyRequestV2] = { + * Converts a JSON-LD request to a [[CreatePropertyRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[CreatePropertyRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[CreatePropertyRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -423,35 +442,38 @@ object CreatePropertyRequestV2 extends KnoraJsonLDRequestReaderV2[CreateProperty requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): CreatePropertyRequestV2 = { + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): CreatePropertyRequestV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // Get the property definition and the ontology's last modification date from the JSON-LD. - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) - val propertyInfoContent = propertyUpdateInfo.propertyInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) + val propertyInfoContent = propertyUpdateInfo.propertyInfoContent val lastModificationDate = propertyUpdateInfo.lastModificationDate // Check that the knora-api:subjectType (if provided) and the knora-api:objectType point to valid entity IRIs. propertyInfoContent.predicates.get(OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri).foreach { subjectTypePred => - val subjectType = subjectTypePred.requireIriObject( - throw BadRequestException(s"Missing or invalid object for predicate knora-api:subjectType")) + val subjectType = subjectTypePred + .requireIriObject(throw BadRequestException(s"Missing or invalid object for predicate knora-api:subjectType")) if (!(subjectType.isKnoraApiV2EntityIri && subjectType.getOntologySchema.contains(ApiV2Complex))) { throw BadRequestException(s"Invalid knora-api:subjectType: $subjectType") } } - val objectType = propertyInfoContent.requireIriObject(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri, - throw BadRequestException(s"Missing knora-api:objectType")) + val objectType = propertyInfoContent.requireIriObject( + OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri, + throw BadRequestException(s"Missing knora-api:objectType") + ) if (!(objectType.isKnoraApiV2EntityIri && objectType.getOntologySchema.contains(ApiV2Complex))) { throw BadRequestException(s"Invalid knora-api:objectType: $objectType") @@ -478,47 +500,49 @@ object CreatePropertyRequestV2 extends KnoraJsonLDRequestReaderV2[CreateProperty } /** - * Requests the addition of a class to an ontology. A successful response will be a [[ReadOntologyV2]]. - * - * @param classInfoContent a [[ClassInfoContentV2]] containing the class definition. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class CreateClassRequestV2(classInfoContent: ClassInfoContentV2, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests the addition of a class to an ontology. A successful response will be a [[ReadOntologyV2]]. + * + * @param classInfoContent a [[ClassInfoContentV2]] containing the class definition. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class CreateClassRequestV2( + classInfoContent: ClassInfoContentV2, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Constructs instances of [[CreateClassRequestV2]] based on JSON-LD requests. - */ + * Constructs instances of [[CreateClassRequestV2]] based on JSON-LD requests. + */ object CreateClassRequestV2 extends KnoraJsonLDRequestReaderV2[CreateClassRequestV2] { /** - * Converts a JSON-LD request to a [[CreateClassRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[CreateClassRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[CreateClassRequestV2] = { + * Converts a JSON-LD request to a [[CreateClassRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[CreateClassRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[CreateClassRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -527,19 +551,20 @@ object CreateClassRequestV2 extends KnoraJsonLDRequestReaderV2[CreateClassReques requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): CreateClassRequestV2 = { + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): CreateClassRequestV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // Get the class definition and the ontology's last modification date from the JSON-LD. - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate // The request must provide an rdfs:label and an rdfs:comment. @@ -563,48 +588,49 @@ object CreateClassRequestV2 extends KnoraJsonLDRequestReaderV2[CreateClassReques } /** - * Requests the addition of cardinalities to a class. A successful response will be a [[ReadOntologyV2]]. - * - * @param classInfoContent a [[ClassInfoContentV2]] containing the class definition. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class AddCardinalitiesToClassRequestV2(classInfoContent: ClassInfoContentV2, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests the addition of cardinalities to a class. A successful response will be a [[ReadOntologyV2]]. + * + * @param classInfoContent a [[ClassInfoContentV2]] containing the class definition. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class AddCardinalitiesToClassRequestV2( + classInfoContent: ClassInfoContentV2, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Constructs instances of [[AddCardinalitiesToClassRequestV2]] based on JSON-LD input. - */ + * Constructs instances of [[AddCardinalitiesToClassRequestV2]] based on JSON-LD input. + */ object AddCardinalitiesToClassRequestV2 extends KnoraJsonLDRequestReaderV2[AddCardinalitiesToClassRequestV2] { /** - * Converts JSON-LD input into an [[AddCardinalitiesToClassRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return an [[AddCardinalitiesToClassRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[AddCardinalitiesToClassRequestV2] = { + * Converts JSON-LD input into an [[AddCardinalitiesToClassRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return an [[AddCardinalitiesToClassRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[AddCardinalitiesToClassRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -613,17 +639,18 @@ object AddCardinalitiesToClassRequestV2 extends KnoraJsonLDRequestReaderV2[AddCa requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): AddCardinalitiesToClassRequestV2 = { + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): AddCardinalitiesToClassRequestV2 = { // Get the class definition and the ontology's last modification date from the JSON-LD. - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate // The request must provide cardinalities. @@ -643,60 +670,62 @@ object AddCardinalitiesToClassRequestV2 extends KnoraJsonLDRequestReaderV2[AddCa } /** - * Checks whether the cardinalities of a class can be replaced. A successful response will be a [[CanDoResponseV2]]. - * - * @param classIri the class IRI. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class CanChangeCardinalitiesRequestV2(classIri: SmartIri, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Checks whether the cardinalities of a class can be replaced. A successful response will be a [[CanDoResponseV2]]. + * + * @param classIri the class IRI. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class CanChangeCardinalitiesRequestV2( + classIri: SmartIri, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Requests the replacement of a class's cardinalities with new ones. A successful response will be a [[ReadOntologyV2]]. - * - * @param classInfoContent a [[ClassInfoContentV2]] containing the new cardinalities. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class ChangeCardinalitiesRequestV2(classInfoContent: ClassInfoContentV2, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests the replacement of a class's cardinalities with new ones. A successful response will be a [[ReadOntologyV2]]. + * + * @param classInfoContent a [[ClassInfoContentV2]] containing the new cardinalities. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class ChangeCardinalitiesRequestV2( + classInfoContent: ClassInfoContentV2, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Constructs instances of [[ChangeCardinalitiesRequestV2]] based on JSON-LD input. - */ + * Constructs instances of [[ChangeCardinalitiesRequestV2]] based on JSON-LD input. + */ object ChangeCardinalitiesRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeCardinalitiesRequestV2] { /** - * Converts JSON-LD input into a [[ChangeCardinalitiesRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[ChangeCardinalitiesRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ChangeCardinalitiesRequestV2] = { + * Converts JSON-LD input into a [[ChangeCardinalitiesRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[ChangeCardinalitiesRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ChangeCardinalitiesRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -705,15 +734,16 @@ object ChangeCardinalitiesRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeCar requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): ChangeCardinalitiesRequestV2 = { - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): ChangeCardinalitiesRequestV2 = { + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate ChangeCardinalitiesRequestV2( @@ -727,123 +757,279 @@ object ChangeCardinalitiesRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeCar } /** - * Requests the deletion of a class. A successful response will be a [[ReadOntologyMetadataV2]]. - * - * @param classIri the IRI of the class to be deleted. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class DeleteClassRequestV2(classIri: SmartIri, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * FIXME(DSP-1856): Can only remove one single cardinality at a time. + * Requests a check if the user can remove class's cardinalities. A successful response will be a [[CanDoResponseV2]]. + * + * @param classInfoContent a [[ClassInfoContentV2]] containing the cardinalities to be removed. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +final case class CanDeleteCardinalitiesFromClassRequestV2( + classInfoContent: ClassInfoContentV2, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Asks whether a class can be deleted. A successful response will be a [[CanDoResponseV2]]. - * - * @param classIri the IRI of the class to be deleted. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class CanDeleteClassRequestV2(classIri: SmartIri, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Constructs instances of [[CanDeleteCardinalitiesFromClassRequestV2]] based on JSON-LD input. + */ +object CanDeleteCardinalitiesFromClassRequestV2 + extends KnoraJsonLDRequestReaderV2[CanDeleteCardinalitiesFromClassRequestV2] { + + /** + * Converts JSON-LD input into a [[DeleteCardinalitiesFromClassRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[DeleteCardinalitiesFromClassRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[CanDeleteCardinalitiesFromClassRequestV2] = + Future { + fromJsonLDSync( + jsonLDDocument = jsonLDDocument, + apiRequestID = apiRequestID, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + } + + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): CanDeleteCardinalitiesFromClassRequestV2 = { + val inputOntology = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntology) + val classInfoContent = classUpdateInfo.classInfoContent + val lastModificationDate = classUpdateInfo.lastModificationDate + + CanDeleteCardinalitiesFromClassRequestV2( + classInfoContent = classInfoContent, + lastModificationDate = lastModificationDate, + apiRequestID = apiRequestID, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + } +} /** - * Requests the deletion of a property. A successful response will be a [[ReadOntologyMetadataV2]]. - * - * @param propertyIri the IRI of the property to be deleted. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class DeletePropertyRequestV2(propertyIri: SmartIri, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * FIXME(DSP-1856): Can only remove one single cardinality at a time. + * Requests the removal of a class's cardinalities. A successful response will be a [[ReadOntologyV2]]. + * + * @param classInfoContent a [[ClassInfoContentV2]] containing the cardinalities to be removed. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +final case class DeleteCardinalitiesFromClassRequestV2( + classInfoContent: ClassInfoContentV2, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Asks whether a property can be deleted. A successful response will be a [[CanDoResponseV2]]. - * - * @param propertyIri the IRI of the property to be deleted. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class CanDeletePropertyRequestV2(propertyIri: SmartIri, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Constructs instances of [[DeleteCardinalitiesFromClassRequestV2]] based on JSON-LD input. + */ +object DeleteCardinalitiesFromClassRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteCardinalitiesFromClassRequestV2] { + + /** + * Converts JSON-LD input into a [[DeleteCardinalitiesFromClassRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[DeleteCardinalitiesFromClassRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[DeleteCardinalitiesFromClassRequestV2] = + Future { + fromJsonLDSync( + jsonLDDocument = jsonLDDocument, + apiRequestID = apiRequestID, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + } + + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): DeleteCardinalitiesFromClassRequestV2 = { + val inputOntology = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntology) + val classInfoContent = classUpdateInfo.classInfoContent + val lastModificationDate = classUpdateInfo.lastModificationDate + + DeleteCardinalitiesFromClassRequestV2( + classInfoContent = classInfoContent, + lastModificationDate = lastModificationDate, + apiRequestID = apiRequestID, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + } +} + +/** + * Requests the deletion of a class. A successful response will be a [[ReadOntologyMetadataV2]]. + * + * @param classIri the IRI of the class to be deleted. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class DeleteClassRequestV2( + classIri: SmartIri, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 + +/** + * Asks whether a class can be deleted. A successful response will be a [[CanDoResponseV2]]. + * + * @param classIri the IRI of the class to be deleted. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class CanDeleteClassRequestV2( + classIri: SmartIri, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 + +/** + * Requests the deletion of a property. A successful response will be a [[ReadOntologyMetadataV2]]. + * + * @param propertyIri the IRI of the property to be deleted. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class DeletePropertyRequestV2( + propertyIri: SmartIri, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * A trait for requests to change entity labels or comments. - */ + * Asks whether a property can be deleted. A successful response will be a [[CanDoResponseV2]]. + * + * @param propertyIri the IRI of the property to be deleted. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class CanDeletePropertyRequestV2( + propertyIri: SmartIri, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 + +/** + * A trait for requests to change entity labels or comments. + */ sealed trait ChangeLabelsOrCommentsRequest { /** - * The predicate to update: `rdfs:label` or `rdfs:comment`. - */ + * The predicate to update: `rdfs:label` or `rdfs:comment`. + */ val predicateToUpdate: SmartIri /** - * The new objects of the predicate. - */ + * The new objects of the predicate. + */ val newObjects: Seq[StringLiteralV2] } /** - * Requests that the `salsah-gui:guiElement` and `salsah-gui:guiAttribute` of a property are changed. - * - * @param propertyIri the IRI of the property to be changed. - * @param newGuiElement the new GUI element to be used with the property, or `None` if no GUI element should be specified. - * @param newGuiAttributes the new GUI attributes to be used with the property, or `None` if no GUI element should be specified. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ChangePropertyGuiElementRequest(propertyIri: SmartIri, - newGuiElement: Option[SmartIri], - newGuiAttributes: Set[String], - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 - -/** - * Constructs instances of [[ChangePropertyGuiElementRequest]] based on JSON-LD input. - */ + * Requests that the `salsah-gui:guiElement` and `salsah-gui:guiAttribute` of a property are changed. + * + * @param propertyIri the IRI of the property to be changed. + * @param newGuiElement the new GUI element to be used with the property, or `None` if no GUI element should be specified. + * @param newGuiAttributes the new GUI attributes to be used with the property, or `None` if no GUI element should be specified. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ChangePropertyGuiElementRequest( + propertyIri: SmartIri, + newGuiElement: Option[SmartIri], + newGuiAttributes: Set[String], + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 + +/** + * Constructs instances of [[ChangePropertyGuiElementRequest]] based on JSON-LD input. + */ object ChangePropertyGuiElementRequest extends KnoraJsonLDRequestReaderV2[ChangePropertyGuiElementRequest] { /** - * Converts a JSON-LD request to a [[ChangePropertyGuiElementRequest]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[ChangePropertyLabelsOrCommentsRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ChangePropertyGuiElementRequest] = { + * Converts a JSON-LD request to a [[ChangePropertyGuiElementRequest]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[ChangePropertyLabelsOrCommentsRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ChangePropertyGuiElementRequest] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -852,17 +1038,18 @@ object ChangePropertyGuiElementRequest extends KnoraJsonLDRequestReaderV2[Change requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): ChangePropertyGuiElementRequest = { + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): ChangePropertyGuiElementRequest = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) - val propertyInfoContent = propertyUpdateInfo.propertyInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) + val propertyInfoContent = propertyUpdateInfo.propertyInfoContent val lastModificationDate = propertyUpdateInfo.lastModificationDate val newGuiElement: Option[SmartIri] = @@ -900,54 +1087,55 @@ object ChangePropertyGuiElementRequest extends KnoraJsonLDRequestReaderV2[Change } /** - * Requests that a property's labels or comments are changed. A successful response will be a [[ReadOntologyV2]]. - * - * @param propertyIri the IRI of the property. - * @param predicateToUpdate `rdfs:label` or `rdfs:comment`. - * @param newObjects the property's new labels or comments. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class ChangePropertyLabelsOrCommentsRequestV2(propertyIri: SmartIri, - predicateToUpdate: SmartIri, - newObjects: Seq[StringLiteralV2], - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests that a property's labels or comments are changed. A successful response will be a [[ReadOntologyV2]]. + * + * @param propertyIri the IRI of the property. + * @param predicateToUpdate `rdfs:label` or `rdfs:comment`. + * @param newObjects the property's new labels or comments. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class ChangePropertyLabelsOrCommentsRequestV2( + propertyIri: SmartIri, + predicateToUpdate: SmartIri, + newObjects: Seq[StringLiteralV2], + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 with ChangeLabelsOrCommentsRequest /** - * Constructs instances of [[ChangePropertyLabelsOrCommentsRequestV2]] based on JSON-LD input. - */ + * Constructs instances of [[ChangePropertyLabelsOrCommentsRequestV2]] based on JSON-LD input. + */ object ChangePropertyLabelsOrCommentsRequestV2 extends KnoraJsonLDRequestReaderV2[ChangePropertyLabelsOrCommentsRequestV2] { /** - * Converts a JSON-LD request to a [[ChangePropertyLabelsOrCommentsRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[ChangePropertyLabelsOrCommentsRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ChangePropertyLabelsOrCommentsRequestV2] = { + * Converts a JSON-LD request to a [[ChangePropertyLabelsOrCommentsRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[ChangePropertyLabelsOrCommentsRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ChangePropertyLabelsOrCommentsRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -956,23 +1144,24 @@ object ChangePropertyLabelsOrCommentsRequestV2 requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): ChangePropertyLabelsOrCommentsRequestV2 = { - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) - val propertyInfoContent = propertyUpdateInfo.propertyInfoContent - val lastModificationDate = propertyUpdateInfo.lastModificationDate + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): ChangePropertyLabelsOrCommentsRequestV2 = { + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) + val propertyInfoContent = propertyUpdateInfo.propertyInfoContent + val lastModificationDate = propertyUpdateInfo.lastModificationDate val predicateInfoToUpdate = OntologyUpdateHelper.getLabelsOrComments(propertyInfoContent) ChangePropertyLabelsOrCommentsRequestV2( propertyIri = propertyInfoContent.propertyIri, predicateToUpdate = predicateInfoToUpdate.predicateIri, - newObjects = predicateInfoToUpdate.objects.collect { - case strLiteral: StringLiteralV2 => strLiteral + newObjects = predicateInfoToUpdate.objects.collect { case strLiteral: StringLiteralV2 => + strLiteral }, lastModificationDate = lastModificationDate, apiRequestID = apiRequestID, @@ -983,53 +1172,54 @@ object ChangePropertyLabelsOrCommentsRequestV2 } /** - * Requests that a class's labels or comments are changed. A successful response will be a [[ReadOntologyV2]]. - * - * @param classIri the IRI of the property. - * @param predicateToUpdate `rdfs:label` or `rdfs:comment`. - * @param newObjects the class's new labels or comments. - * @param lastModificationDate the ontology's last modification date. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class ChangeClassLabelsOrCommentsRequestV2(classIri: SmartIri, - predicateToUpdate: SmartIri, - newObjects: Seq[StringLiteralV2], - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests that a class's labels or comments are changed. A successful response will be a [[ReadOntologyV2]]. + * + * @param classIri the IRI of the property. + * @param predicateToUpdate `rdfs:label` or `rdfs:comment`. + * @param newObjects the class's new labels or comments. + * @param lastModificationDate the ontology's last modification date. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class ChangeClassLabelsOrCommentsRequestV2( + classIri: SmartIri, + predicateToUpdate: SmartIri, + newObjects: Seq[StringLiteralV2], + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 with ChangeLabelsOrCommentsRequest /** - * Constructs instances of [[ChangeClassLabelsOrCommentsRequestV2]] based on JSON-LD input. - */ + * Constructs instances of [[ChangeClassLabelsOrCommentsRequestV2]] based on JSON-LD input. + */ object ChangeClassLabelsOrCommentsRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeClassLabelsOrCommentsRequestV2] { /** - * Converts a JSON-LD request to a [[ChangeClassLabelsOrCommentsRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[ChangeClassLabelsOrCommentsRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ChangeClassLabelsOrCommentsRequestV2] = { + * Converts a JSON-LD request to a [[ChangeClassLabelsOrCommentsRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[ChangeClassLabelsOrCommentsRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ChangeClassLabelsOrCommentsRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -1038,23 +1228,24 @@ object ChangeClassLabelsOrCommentsRequestV2 extends KnoraJsonLDRequestReaderV2[C requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): ChangeClassLabelsOrCommentsRequestV2 = { - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent - val lastModificationDate = classUpdateInfo.lastModificationDate + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): ChangeClassLabelsOrCommentsRequestV2 = { + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent + val lastModificationDate = classUpdateInfo.lastModificationDate val predicateInfoToUpdate = OntologyUpdateHelper.getLabelsOrComments(classInfoContent) ChangeClassLabelsOrCommentsRequestV2( classIri = classInfoContent.classIri, predicateToUpdate = predicateInfoToUpdate.predicateIri, - newObjects = predicateInfoToUpdate.objects.collect { - case strLiteral: StringLiteralV2 => strLiteral + newObjects = predicateInfoToUpdate.objects.collect { case strLiteral: StringLiteralV2 => + strLiteral }, lastModificationDate = lastModificationDate, apiRequestID = apiRequestID, @@ -1064,23 +1255,25 @@ object ChangeClassLabelsOrCommentsRequestV2 extends KnoraJsonLDRequestReaderV2[C } } -case class ChangeGuiOrderRequestV2(classInfoContent: ClassInfoContentV2, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 +case class ChangeGuiOrderRequestV2( + classInfoContent: ClassInfoContentV2, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 object ChangeGuiOrderRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeGuiOrderRequestV2] { - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ChangeGuiOrderRequestV2] = { + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ChangeGuiOrderRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -1089,17 +1282,18 @@ object ChangeGuiOrderRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeGuiOrder requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): ChangeGuiOrderRequestV2 = { + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): ChangeGuiOrderRequestV2 = { // Get the class definition and the ontology's last modification date from the JSON-LD. - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate // The request must provide cardinalities. @@ -1119,52 +1313,53 @@ object ChangeGuiOrderRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeGuiOrder } /** - * Requests a change in the metadata of an ontology. A successful response will be a [[ReadOntologyMetadataV2]]. - * - * @param ontologyIri the external ontology IRI. - * @param label the ontology's new label. - * @param comment the ontology's new comment. - * @param lastModificationDate the ontology's last modification date, returned in a previous operation. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class ChangeOntologyMetadataRequestV2(ontologyIri: SmartIri, - label: Option[String] = None, - comment: Option[String] = None, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 - -/** - * Constructs instances of [[ChangeOntologyMetadataRequestV2]] based on JSON-LD requests. - */ + * Requests a change in the metadata of an ontology. A successful response will be a [[ReadOntologyMetadataV2]]. + * + * @param ontologyIri the external ontology IRI. + * @param label the ontology's new label. + * @param comment the ontology's new comment. + * @param lastModificationDate the ontology's last modification date, returned in a previous operation. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class ChangeOntologyMetadataRequestV2( + ontologyIri: SmartIri, + label: Option[String] = None, + comment: Option[String] = None, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 + +/** + * Constructs instances of [[ChangeOntologyMetadataRequestV2]] based on JSON-LD requests. + */ object ChangeOntologyMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeOntologyMetadataRequestV2] { /** - * Converts a JSON-LD request to a [[ChangeOntologyMetadataRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[ChangeClassLabelsOrCommentsRequestV2]] representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ChangeOntologyMetadataRequestV2] = { + * Converts a JSON-LD request to a [[ChangeOntologyMetadataRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[ChangeClassLabelsOrCommentsRequestV2]] representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ChangeOntologyMetadataRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -1173,19 +1368,21 @@ object ChangeOntologyMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[Change requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): ChangeOntologyMetadataRequestV2 = { - val inputOntologyV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val inputMetadata = inputOntologyV2.ontologyMetadata - val ontologyIri = inputMetadata.ontologyIri - val label: Option[String] = inputMetadata.label + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): ChangeOntologyMetadataRequestV2 = { + val inputOntologyV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val inputMetadata = inputOntologyV2.ontologyMetadata + val ontologyIri = inputMetadata.ontologyIri + val label: Option[String] = inputMetadata.label val comment: Option[String] = inputMetadata.comment val lastModificationDate = inputMetadata.lastModificationDate.getOrElse( - throw BadRequestException("No knora-api:lastModificationDate submitted")) + throw BadRequestException("No knora-api:lastModificationDate submitted") + ) ChangeOntologyMetadataRequestV2( ontologyIri = ontologyIri, @@ -1200,217 +1397,226 @@ object ChangeOntologyMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[Change } /** - * Deletes the comment from an ontology. A successful response will be a [[ReadOntologyMetadataV2]]. - * - * @param ontologyIri the external ontology IRI. - * @param lastModificationDate the ontology's last modification date, returned in a previous operation. - * @param apiRequestID the ID of the API request. - * @param requestingUser the user making the request. - */ -case class DeleteOntologyCommentRequestV2(ontologyIri: SmartIri, - lastModificationDate: Instant, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Deletes the comment from an ontology. A successful response will be a [[ReadOntologyMetadataV2]]. + * + * @param ontologyIri the external ontology IRI. + * @param lastModificationDate the ontology's last modification date, returned in a previous operation. + * @param apiRequestID the ID of the API request. + * @param requestingUser the user making the request. + */ +case class DeleteOntologyCommentRequestV2( + ontologyIri: SmartIri, + lastModificationDate: Instant, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Requests all available information about a list of ontology entities (classes and/or properties). A successful response will be an - * [[EntityInfoGetResponseV2]]. - * - * @param classIris the IRIs of the class entities to be queried. - * @param propertyIris the IRIs of the property entities to be queried. - * @param requestingUser the user making the request. - */ -case class EntityInfoGetRequestV2(classIris: Set[SmartIri] = Set.empty[SmartIri], - propertyIris: Set[SmartIri] = Set.empty[SmartIri], - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests all available information about a list of ontology entities (classes and/or properties). A successful response will be an + * [[EntityInfoGetResponseV2]]. + * + * @param classIris the IRIs of the class entities to be queried. + * @param propertyIris the IRIs of the property entities to be queried. + * @param requestingUser the user making the request. + */ +case class EntityInfoGetRequestV2( + classIris: Set[SmartIri] = Set.empty[SmartIri], + propertyIris: Set[SmartIri] = Set.empty[SmartIri], + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Represents assertions about one or more ontology entities (resource classes and/or properties). - * - * @param classInfoMap a [[Map]] of class entity IRIs to [[ReadClassInfoV2]] objects. - * @param propertyInfoMap a [[Map]] of property entity IRIs to [[ReadPropertyInfoV2]] objects. - */ -case class EntityInfoGetResponseV2(classInfoMap: Map[SmartIri, ReadClassInfoV2], - propertyInfoMap: Map[SmartIri, ReadPropertyInfoV2]) - -/** - * Requests all available information about a list of ontology entities (standoff classes and/or properties). A successful response will be an - * [[StandoffEntityInfoGetResponseV2]]. - * - * @param standoffClassIris the IRIs of the resource entities to be queried. - * @param standoffPropertyIris the IRIs of the property entities to be queried. - * @param requestingUser the user making the request. - */ -case class StandoffEntityInfoGetRequestV2(standoffClassIris: Set[SmartIri] = Set.empty[SmartIri], - standoffPropertyIris: Set[SmartIri] = Set.empty[SmartIri], - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Represents assertions about one or more ontology entities (resource classes and/or properties). + * + * @param classInfoMap a [[Map]] of class entity IRIs to [[ReadClassInfoV2]] objects. + * @param propertyInfoMap a [[Map]] of property entity IRIs to [[ReadPropertyInfoV2]] objects. + */ +case class EntityInfoGetResponseV2( + classInfoMap: Map[SmartIri, ReadClassInfoV2], + propertyInfoMap: Map[SmartIri, ReadPropertyInfoV2] +) /** - * Represents assertions about one or more ontology entities (resource classes and/or properties). - * - * @param standoffClassInfoMap a [[Map]] of standoff class IRIs to [[ReadClassInfoV2]] objects. - * @param standoffPropertyInfoMap a [[Map]] of standoff property IRIs to [[ReadPropertyInfoV2]] objects. - */ -case class StandoffEntityInfoGetResponseV2(standoffClassInfoMap: Map[SmartIri, ReadClassInfoV2], - standoffPropertyInfoMap: Map[SmartIri, ReadPropertyInfoV2]) + * Requests all available information about a list of ontology entities (standoff classes and/or properties). A successful response will be an + * [[StandoffEntityInfoGetResponseV2]]. + * + * @param standoffClassIris the IRIs of the resource entities to be queried. + * @param standoffPropertyIris the IRIs of the property entities to be queried. + * @param requestingUser the user making the request. + */ +case class StandoffEntityInfoGetRequestV2( + standoffClassIris: Set[SmartIri] = Set.empty[SmartIri], + standoffPropertyIris: Set[SmartIri] = Set.empty[SmartIri], + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Requests information about all standoff classes that are a subclass of a data type standoff class. A successful response will be an - * [[StandoffClassesWithDataTypeGetResponseV2]]. - * - * @param requestingUser the user making the request. - */ + * Represents assertions about one or more ontology entities (resource classes and/or properties). + * + * @param standoffClassInfoMap a [[Map]] of standoff class IRIs to [[ReadClassInfoV2]] objects. + * @param standoffPropertyInfoMap a [[Map]] of standoff property IRIs to [[ReadPropertyInfoV2]] objects. + */ +case class StandoffEntityInfoGetResponseV2( + standoffClassInfoMap: Map[SmartIri, ReadClassInfoV2], + standoffPropertyInfoMap: Map[SmartIri, ReadPropertyInfoV2] +) + +/** + * Requests information about all standoff classes that are a subclass of a data type standoff class. A successful response will be an + * [[StandoffClassesWithDataTypeGetResponseV2]]. + * + * @param requestingUser the user making the request. + */ case class StandoffClassesWithDataTypeGetRequestV2(requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Represents assertions about all standoff classes that are a subclass of a data type standoff class. - * - * @param standoffClassInfoMap a [[Map]] of standoff class entity IRIs to [[ReadClassInfoV2]] objects. - */ + * Represents assertions about all standoff classes that are a subclass of a data type standoff class. + * + * @param standoffClassInfoMap a [[Map]] of standoff class entity IRIs to [[ReadClassInfoV2]] objects. + */ case class StandoffClassesWithDataTypeGetResponseV2(standoffClassInfoMap: Map[SmartIri, ReadClassInfoV2]) /** - * Requests information about all standoff property entities. A successful response will be an - * [[StandoffAllPropertyEntitiesGetResponseV2]]. - * - * @param requestingUser the user making the request. - */ + * Requests information about all standoff property entities. A successful response will be an + * [[StandoffAllPropertyEntitiesGetResponseV2]]. + * + * @param requestingUser the user making the request. + */ case class StandoffAllPropertyEntitiesGetRequestV2(requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Represents assertions about all standoff all standoff property entities. - * - * @param standoffAllPropertiesEntityInfoMap a [[Map]] of standoff property IRIs to [[ReadPropertyInfoV2]] objects. - */ + * Represents assertions about all standoff all standoff property entities. + * + * @param standoffAllPropertiesEntityInfoMap a [[Map]] of standoff property IRIs to [[ReadPropertyInfoV2]] objects. + */ case class StandoffAllPropertyEntitiesGetResponseV2( - standoffAllPropertiesEntityInfoMap: Map[SmartIri, ReadPropertyInfoV2]) + standoffAllPropertiesEntityInfoMap: Map[SmartIri, ReadPropertyInfoV2] +) /** - * Checks whether a Knora resource or value class is a subclass of (or identical to) another class. - * A successful response will be a [[CheckSubClassResponseV2]]. - * - * @param subClassIri the IRI of the subclass. - * @param superClassIri the IRI of the superclass. - */ + * Checks whether a Knora resource or value class is a subclass of (or identical to) another class. + * A successful response will be a [[CheckSubClassResponseV2]]. + * + * @param subClassIri the IRI of the subclass. + * @param superClassIri the IRI of the superclass. + */ case class CheckSubClassRequestV2(subClassIri: SmartIri, superClassIri: SmartIri, requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Represents a response to a [[CheckSubClassRequestV2]]. - * - * @param isSubClass `true` if the requested inheritance relationship exists. - */ + * Represents a response to a [[CheckSubClassRequestV2]]. + * + * @param isSubClass `true` if the requested inheritance relationship exists. + */ case class CheckSubClassResponseV2(isSubClass: Boolean) /** - * Requests information about the subclasses of a Knora resource class. A successful response will be - * a [[SubClassesGetResponseV2]]. - * - * @param resourceClassIri the IRI of the given resource class. - * @param requestingUser the user making the request. - */ + * Requests information about the subclasses of a Knora resource class. A successful response will be + * a [[SubClassesGetResponseV2]]. + * + * @param resourceClassIri the IRI of the given resource class. + * @param requestingUser the user making the request. + */ case class SubClassesGetRequestV2(resourceClassIri: SmartIri, requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Provides information about the subclasses of a Knora resource class. - * - * @param subClasses a list of [[SubClassInfoV2]] representing the subclasses of the specified class. - */ + * Provides information about the subclasses of a Knora resource class. + * + * @param subClasses a list of [[SubClassInfoV2]] representing the subclasses of the specified class. + */ case class SubClassesGetResponseV2(subClasses: Seq[SubClassInfoV2]) /** - * - * Request information about the Knora entities (Knora resource classes, standoff class, resource properties, and standoff properties) of a named graph. - * A successful response will be a [[OntologyKnoraEntitiesIriInfoV2]]. - * - * @param ontologyIri the IRI of the named graph. - * @param requestingUser the user making the request. - */ + * Request information about the Knora entities (Knora resource classes, standoff class, resource properties, and standoff properties) of a named graph. + * A successful response will be a [[OntologyKnoraEntitiesIriInfoV2]]. + * + * @param ontologyIri the IRI of the named graph. + * @param requestingUser the user making the request. + */ case class OntologyKnoraEntityIrisGetRequestV2(ontologyIri: SmartIri, requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Requests metadata about ontologies by project. - * - * @param projectIris the IRIs of the projects for which ontologies should be returned. If this set is empty, information - * about all ontologies is returned. - * @param requestingUser the user making the request. - */ -case class OntologyMetadataGetByProjectRequestV2(projectIris: Set[SmartIri] = Set.empty[SmartIri], - requestingUser: UserADM) - extends OntologiesResponderRequestV2 + * Requests metadata about ontologies by project. + * + * @param projectIris the IRIs of the projects for which ontologies should be returned. If this set is empty, information + * about all ontologies is returned. + * @param requestingUser the user making the request. + */ +case class OntologyMetadataGetByProjectRequestV2( + projectIris: Set[SmartIri] = Set.empty[SmartIri], + requestingUser: UserADM +) extends OntologiesResponderRequestV2 /** - * Requests metadata about ontologies by ontology IRI. - * - * @param ontologyIris the IRIs of the ontologies to be queried. If this set is empty, information - * about all ontologies is returned. - * @param requestingUser the user making the request. - */ + * Requests metadata about ontologies by ontology IRI. + * + * @param ontologyIris the IRIs of the ontologies to be queried. If this set is empty, information + * about all ontologies is returned. + * @param requestingUser the user making the request. + */ case class OntologyMetadataGetByIriRequestV2(ontologyIris: Set[SmartIri] = Set.empty[SmartIri], requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Requests entity definitions for the given ontology. - * - * @param ontologyIri the ontology to query for. - * @param allLanguages true if information in all available languages should be returned. - * @param requestingUser the user making the request. - */ + * Requests entity definitions for the given ontology. + * + * @param ontologyIri the ontology to query for. + * @param allLanguages true if information in all available languages should be returned. + * @param requestingUser the user making the request. + */ case class OntologyEntitiesGetRequestV2(ontologyIri: SmartIri, allLanguages: Boolean, requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Requests the entity definitions for the given class IRIs. A successful response will be a [[ReadOntologyV2]]. - * - * @param classIris the IRIs of the classes to be queried. - * @param allLanguages true if information in all available languages should be returned. - * @param requestingUser the user making the request. - */ + * Requests the entity definitions for the given class IRIs. A successful response will be a [[ReadOntologyV2]]. + * + * @param classIris the IRIs of the classes to be queried. + * @param allLanguages true if information in all available languages should be returned. + * @param requestingUser the user making the request. + */ case class ClassesGetRequestV2(classIris: Set[SmartIri], allLanguages: Boolean, requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Requests the definitions of the specified properties. A successful response will be a [[ReadOntologyV2]]. - * - * @param propertyIris the IRIs of the properties to be queried. - * @param allLanguages true if information in all available languages should be returned. - * @param requestingUser the user making the request. - */ + * Requests the definitions of the specified properties. A successful response will be a [[ReadOntologyV2]]. + * + * @param propertyIris the IRIs of the properties to be queried. + * @param allLanguages true if information in all available languages should be returned. + * @param requestingUser the user making the request. + */ case class PropertiesGetRequestV2(propertyIris: Set[SmartIri], allLanguages: Boolean, requestingUser: UserADM) extends OntologiesResponderRequestV2 /** - * Represents the contents of an ontology to be returned in an API response. - * - * @param ontologyMetadata metadata about the ontology. - * @param classes information about classes. - * @param properties information about properties. - * @param isWholeOntology `true` if this is the whole specified ontology, `false` if it's just selected entities. - * @param userLang the preferred language in which the information should be returned, or [[None]] if information - * should be returned in all available languages. - */ -case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, - classes: Map[SmartIri, ReadClassInfoV2] = Map.empty[SmartIri, ReadClassInfoV2], - properties: Map[SmartIri, ReadPropertyInfoV2] = Map.empty[SmartIri, ReadPropertyInfoV2], - individuals: Map[SmartIri, ReadIndividualInfoV2] = Map.empty[SmartIri, ReadIndividualInfoV2], - isWholeOntology: Boolean = false, - userLang: Option[String] = None) - extends KnoraJsonLDResponseV2 + * Represents the contents of an ontology to be returned in an API response. + * + * @param ontologyMetadata metadata about the ontology. + * @param classes information about classes. + * @param properties information about properties. + * @param isWholeOntology `true` if this is the whole specified ontology, `false` if it's just selected entities. + * @param userLang the preferred language in which the information should be returned, or [[None]] if information + * should be returned in all available languages. + */ +case class ReadOntologyV2( + ontologyMetadata: OntologyMetadataV2, + classes: Map[SmartIri, ReadClassInfoV2] = Map.empty[SmartIri, ReadClassInfoV2], + properties: Map[SmartIri, ReadPropertyInfoV2] = Map.empty[SmartIri, ReadPropertyInfoV2], + individuals: Map[SmartIri, ReadIndividualInfoV2] = Map.empty[SmartIri, ReadIndividualInfoV2], + isWholeOntology: Boolean = false, + userLang: Option[String] = None +) extends KnoraJsonLDResponseV2 with KnoraReadV2[ReadOntologyV2] { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * Converts this [[ReadOntologyV2]] to the specified Knora API v2 schema. - * - * @param targetSchema the target schema. - * @return the converted [[ReadOntologyV2]]. - */ + * Converts this [[ReadOntologyV2]] to the specified Knora API v2 schema. + * + * @param targetSchema the target schema. + * @return the converted [[ReadOntologyV2]]. + */ override def toOntologySchema(targetSchema: ApiV2Schema): ReadOntologyV2 = { // Get rules for transforming internal entities to external entities in the target schema. val transformationRules = @@ -1419,8 +1625,8 @@ case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, // If we're converting to the API v2 simple schema, filter out link value properties. val propertiesConsideringLinkValueProps = targetSchema match { case ApiV2Simple => - properties.filterNot { - case (_, propertyInfo) => propertyInfo.isLinkValueProp + properties.filterNot { case (_, propertyInfo) => + propertyInfo.isLinkValueProp } case _ => properties @@ -1430,15 +1636,18 @@ case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, // exist in the target schema. val (classesFilteredForTargetSchema, propsFilteredForTargetSchema) = - if (ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraBase.KnoraBaseOntologyIri || - ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraAdmin.KnoraAdminOntologyIri) { - val filteredClasses = classes.filterNot { - case (classIri, classDef) => - transformationRules.internalClassesToRemove.contains(classIri) || (targetSchema == ApiV2Simple && classDef.isStandoffClass) + if ( + ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraBase.KnoraBaseOntologyIri || + ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraAdmin.KnoraAdminOntologyIri + ) { + val filteredClasses = classes.filterNot { case (classIri, classDef) => + transformationRules.internalClassesToRemove.contains( + classIri + ) || (targetSchema == ApiV2Simple && classDef.isStandoffClass) } - val filteredProps = propertiesConsideringLinkValueProps.filterNot { - case (propertyIri, _) => transformationRules.internalPropertiesToRemove.contains(propertyIri) + val filteredProps = propertiesConsideringLinkValueProps.filterNot { case (propertyIri, _) => + transformationRules.internalPropertiesToRemove.contains(propertyIri) } (filteredClasses, filteredProps) @@ -1449,37 +1658,40 @@ case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, // Convert everything to the target schema. val ontologyMetadataInTargetSchema = - if (ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraBase.KnoraBaseOntologyIri || - ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraAdmin.KnoraAdminOntologyIri) { + if ( + ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraBase.KnoraBaseOntologyIri || + ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraAdmin.KnoraAdminOntologyIri + ) { transformationRules.ontologyMetadata } else { ontologyMetadata.toOntologySchema(targetSchema) } - val classesInTargetSchema = classesFilteredForTargetSchema.map { - case (classIri, readClassInfo) => - classIri.toOntologySchema(targetSchema) -> readClassInfo.toOntologySchema(targetSchema) + val classesInTargetSchema = classesFilteredForTargetSchema.map { case (classIri, readClassInfo) => + classIri.toOntologySchema(targetSchema) -> readClassInfo.toOntologySchema(targetSchema) } - val propertiesInTargetSchema = propsFilteredForTargetSchema.map { - case (propertyIri, readPropertyInfo) => - propertyIri.toOntologySchema(targetSchema) -> readPropertyInfo.toOntologySchema(targetSchema) + val propertiesInTargetSchema = propsFilteredForTargetSchema.map { case (propertyIri, readPropertyInfo) => + propertyIri.toOntologySchema(targetSchema) -> readPropertyInfo.toOntologySchema(targetSchema) } - val individualsInTargetSchema = individuals.map { - case (individualIri, readIndividualInfo) => - individualIri.toOntologySchema(targetSchema) -> readIndividualInfo.toOntologySchema(targetSchema) + val individualsInTargetSchema = individuals.map { case (individualIri, readIndividualInfo) => + individualIri.toOntologySchema(targetSchema) -> readIndividualInfo.toOntologySchema(targetSchema) } // If we're converting from the internal schema to an external one, and this is the whole ontology, // add classes and properties that exist in the target schema but not in the source schema. val (classesWithExtraOnesForSchema, propertiesWithExtraOnesForSchema) = - if (isWholeOntology && - (ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraBase.KnoraBaseOntologyIri || - ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraAdmin.KnoraAdminOntologyIri)) { - (classesInTargetSchema ++ transformationRules.externalClassesToAdd, - propertiesInTargetSchema ++ transformationRules.externalPropertiesToAdd) + if ( + isWholeOntology && + (ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraBase.KnoraBaseOntologyIri || + ontologyMetadata.ontologyIri.toString == OntologyConstants.KnoraAdmin.KnoraAdminOntologyIri) + ) { + ( + classesInTargetSchema ++ transformationRules.externalClassesToAdd, + propertiesInTargetSchema ++ transformationRules.externalPropertiesToAdd + ) } else { (classesInTargetSchema, propertiesInTargetSchema) } @@ -1492,49 +1704,46 @@ case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, ) } - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = toOntologySchema(targetSchema).generateJsonLD(targetSchema, settings) - } private def generateJsonLD(targetSchema: ApiV2Schema, settings: KnoraSettingsImpl): JsonLDDocument = { // Get the ontologies of all Knora entities mentioned in class definitions. - val knoraOntologiesFromClasses: Set[SmartIri] = classes.values - .flatMap { classInfo => - val entityIris: Set[SmartIri] = classInfo.allCardinalities.keySet ++ classInfo.entityInfoContent.subClassOf + val knoraOntologiesFromClasses: Set[SmartIri] = classes.values.flatMap { classInfo => + val entityIris: Set[SmartIri] = classInfo.allCardinalities.keySet ++ classInfo.entityInfoContent.subClassOf - entityIris.flatMap { entityIri => - if (entityIri.isKnoraEntityIri) { - Set(entityIri.getOntologyFromEntity) - } else { - Set.empty[SmartIri] - } - } + classInfo.entityInfoContent.classIri.getOntologyFromEntity - } - .toSet + entityIris.flatMap { entityIri => + if (entityIri.isKnoraEntityIri) { + Set(entityIri.getOntologyFromEntity) + } else { + Set.empty[SmartIri] + } + } + classInfo.entityInfoContent.classIri.getOntologyFromEntity + }.toSet .filter(_.isKnoraOntologyIri) // Get the ontologies of all Knora entities mentioned in property definitions. - val knoraOntologiesFromProperties: Set[SmartIri] = properties.values - .flatMap { property => - val entityIris = property.entityInfoContent.subPropertyOf ++ - property.entityInfoContent.getPredicateIriObjects(OntologyConstants.KnoraApiV2Simple.SubjectType.toSmartIri) ++ - property.entityInfoContent.getPredicateIriObjects(OntologyConstants.KnoraApiV2Simple.ObjectType.toSmartIri) ++ - property.entityInfoContent.getPredicateIriObjects(OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri) ++ - property.entityInfoContent.getPredicateIriObjects(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri) - - entityIris.flatMap { entityIri => - if (entityIri.isKnoraEntityIri) { - Set(entityIri.getOntologyFromEntity) - } else { - Set.empty[SmartIri] - } - } + property.entityInfoContent.propertyIri.getOntologyFromEntity - } - .toSet + val knoraOntologiesFromProperties: Set[SmartIri] = properties.values.flatMap { property => + val entityIris = property.entityInfoContent.subPropertyOf ++ + property.entityInfoContent.getPredicateIriObjects(OntologyConstants.KnoraApiV2Simple.SubjectType.toSmartIri) ++ + property.entityInfoContent.getPredicateIriObjects(OntologyConstants.KnoraApiV2Simple.ObjectType.toSmartIri) ++ + property.entityInfoContent.getPredicateIriObjects(OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri) ++ + property.entityInfoContent.getPredicateIriObjects(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri) + + entityIris.flatMap { entityIri => + if (entityIri.isKnoraEntityIri) { + Set(entityIri.getOntologyFromEntity) + } else { + Set.empty[SmartIri] + } + } + property.entityInfoContent.propertyIri.getOntologyFromEntity + }.toSet .filter(_.isKnoraOntologyIri) // Determine which ontology to use as the knora-api prefix expansion. @@ -1547,7 +1756,8 @@ case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, val salsahGuiPrefix: Option[(String, String)] = targetSchema match { case ApiV2Complex => Some( - OntologyConstants.SalsahGui.SalsahGuiOntologyLabel -> OntologyConstants.SalsahGuiApiV2WithValueObjects.SalsahGuiPrefixExpansion) + OntologyConstants.SalsahGui.SalsahGuiOntologyLabel -> OntologyConstants.SalsahGuiApiV2WithValueObjects.SalsahGuiPrefixExpansion + ) case _ => None } @@ -1563,10 +1773,10 @@ case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, val context = JsonLDUtil.makeContext( fixedPrefixes = Map( OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> knoraApiPrefixExpansion, - "rdf" -> OntologyConstants.Rdf.RdfPrefixExpansion, - "rdfs" -> OntologyConstants.Rdfs.RdfsPrefixExpansion, - "owl" -> OntologyConstants.Owl.OwlPrefixExpansion, - "xsd" -> OntologyConstants.Xsd.XsdPrefixExpansion + "rdf" -> OntologyConstants.Rdf.RdfPrefixExpansion, + "rdfs" -> OntologyConstants.Rdfs.RdfsPrefixExpansion, + "owl" -> OntologyConstants.Owl.OwlPrefixExpansion, + "xsd" -> OntologyConstants.Xsd.XsdPrefixExpansion ) ++ salsahGuiPrefix, knoraOntologiesNeedingPrefixes = otherKnoraOntologiesUsed ) @@ -1592,14 +1802,16 @@ case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, val jsonIndividuals: Vector[JsonLDObject] = individuals.values.map { readIndividualInfo => userLang match { case Some(lang) => - readIndividualInfo.toJsonLDWithSingleLanguage(targetSchema = targetSchema, - userLang = lang, - settings = settings) + readIndividualInfo.toJsonLDWithSingleLanguage( + targetSchema = targetSchema, + userLang = lang, + settings = settings + ) case None => readIndividualInfo.toJsonLDWithAllLanguages(targetSchema = targetSchema) } }.toVector - val allEntities = jsonClasses ++ jsonProperties ++ jsonIndividuals + val allEntities = jsonClasses ++ jsonProperties ++ jsonIndividuals val allEntitiesSorted = allEntities.sortBy(_.value(JsonLDKeywords.ID)) // Assemble the JSON-LD document. @@ -1613,109 +1825,107 @@ case class ReadOntologyV2(ontologyMetadata: OntologyMetadataV2, } /** - * Represents information about an ontology received as input, either from the client or from the API server (in - * the case of a test). This information is necessarily less complete than the information in a [[ReadOntologyV2]], - * which takes advantage of additional knowledge that is available from the triplestore. - * - * @param ontologyMetadata metadata about the ontology. - * @param classes information about classes in the ontology. - * @param properties information about properties in the ontology. - * @param individuals information about OWL named individuals in the ontology. - */ + * Represents information about an ontology received as input, either from the client or from the API server (in + * the case of a test). This information is necessarily less complete than the information in a [[ReadOntologyV2]], + * which takes advantage of additional knowledge that is available from the triplestore. + * + * @param ontologyMetadata metadata about the ontology. + * @param classes information about classes in the ontology. + * @param properties information about properties in the ontology. + * @param individuals information about OWL named individuals in the ontology. + */ case class InputOntologyV2( - ontologyMetadata: OntologyMetadataV2, - classes: Map[SmartIri, ClassInfoContentV2] = Map.empty[SmartIri, ClassInfoContentV2], - properties: Map[SmartIri, PropertyInfoContentV2] = Map.empty[SmartIri, PropertyInfoContentV2], - individuals: Map[SmartIri, IndividualInfoContentV2] = Map.empty[SmartIri, IndividualInfoContentV2]) { + ontologyMetadata: OntologyMetadataV2, + classes: Map[SmartIri, ClassInfoContentV2] = Map.empty[SmartIri, ClassInfoContentV2], + properties: Map[SmartIri, PropertyInfoContentV2] = Map.empty[SmartIri, PropertyInfoContentV2], + individuals: Map[SmartIri, IndividualInfoContentV2] = Map.empty[SmartIri, IndividualInfoContentV2] +) { /** - * Converts this [[InputOntologyV2]] to the specified Knora API v2 schema. - * - * @param targetSchema the target schema. - * @return the converted [[InputOntologyV2]]. - */ - def toOntologySchema(targetSchema: ApiV2Schema): InputOntologyV2 = { + * Converts this [[InputOntologyV2]] to the specified Knora API v2 schema. + * + * @param targetSchema the target schema. + * @return the converted [[InputOntologyV2]]. + */ + def toOntologySchema(targetSchema: ApiV2Schema): InputOntologyV2 = InputOntologyV2( ontologyMetadata = ontologyMetadata.toOntologySchema(targetSchema), - classes = classes.map { - case (classIri, classInfoContent) => - classIri.toOntologySchema(targetSchema) -> classInfoContent.toOntologySchema(targetSchema) + classes = classes.map { case (classIri, classInfoContent) => + classIri.toOntologySchema(targetSchema) -> classInfoContent.toOntologySchema(targetSchema) }, - properties = properties.map { - case (propertyIri, propertyInfoContent) => - propertyIri.toOntologySchema(targetSchema) -> propertyInfoContent.toOntologySchema(targetSchema) + properties = properties.map { case (propertyIri, propertyInfoContent) => + propertyIri.toOntologySchema(targetSchema) -> propertyInfoContent.toOntologySchema(targetSchema) }, - individuals = individuals.map { - case (individualIri, individualInfoContent) => - individualIri.toOntologySchema(targetSchema) -> individualInfoContent.toOntologySchema(targetSchema) + individuals = individuals.map { case (individualIri, individualInfoContent) => + individualIri.toOntologySchema(targetSchema) -> individualInfoContent.toOntologySchema(targetSchema) } ) - } /** - * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used in tests after an update, when the - * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data - * read back from the triplestore (in which predicate objects are not escaped). It is also used in generating - * client API code. - * - * @return a copy of this [[InputOntologyV2]] with all predicate objects unescaped. - */ - def unescape: InputOntologyV2 = { + * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used in tests after an update, when the + * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data + * read back from the triplestore (in which predicate objects are not escaped). It is also used in generating + * client API code. + * + * @return a copy of this [[InputOntologyV2]] with all predicate objects unescaped. + */ + def unescape: InputOntologyV2 = InputOntologyV2( ontologyMetadata = ontologyMetadata.unescape, - classes = classes.map { - case (classIri, classDef) => classIri -> classDef.unescape + classes = classes.map { case (classIri, classDef) => + classIri -> classDef.unescape }, - properties = properties.map { - case (propertyIri, propertyDef) => propertyIri -> propertyDef.unescape + properties = properties.map { case (propertyIri, propertyDef) => + propertyIri -> propertyDef.unescape }, - individuals = individuals.map { - case (individualIri, individualDef) => individualIri -> individualDef.unescape + individuals = individuals.map { case (individualIri, individualDef) => + individualIri -> individualDef.unescape } ) - } } /** - * Represents a parsing mode used by [[InputOntologyV2]]. - */ + * Represents a parsing mode used by [[InputOntologyV2]]. + */ sealed trait InputOntologyParsingModeV2 /** - * A parsing mode that ignores predicates that are present in Knora responses and absent in client input. - * In tests, this allows a Knora response containing an entity to be parsed and compared with the client input - * that was used to create the entity. - */ + * A parsing mode that ignores predicates that are present in Knora responses and absent in client input. + * In tests, this allows a Knora response containing an entity to be parsed and compared with the client input + * that was used to create the entity. + */ case object TestResponseParsingModeV2 extends InputOntologyParsingModeV2 /** - * A parsing mode that rejects data not allowed in client input. - */ + * A parsing mode that rejects data not allowed in client input. + */ case object ClientInputParsingModeV2 extends InputOntologyParsingModeV2 /** - * A parsing mode for parsing everything returned in a Knora ontology response. - */ + * A parsing mode for parsing everything returned in a Knora ontology response. + */ case object KnoraOutputParsingModeV2 extends InputOntologyParsingModeV2 /** - * Processes JSON-LD received either from the client or from the API server. This is intended to support - * two use cases: - * - * 1. When an update request is received, an [[InputOntologyV2]] can be used to construct an update request message. - * 1. In a test, in which the submitted JSON-LD is similar to the server's response, both can be converted to [[InputOntologyV2]] objects for comparison. - */ + * Processes JSON-LD received either from the client or from the API server. This is intended to support + * two use cases: + * + * 1. When an update request is received, an [[InputOntologyV2]] can be used to construct an update request message. + * 1. In a test, in which the submitted JSON-LD is similar to the server's response, both can be converted to [[InputOntologyV2]] objects for comparison. + */ object InputOntologyV2 { /** - * Constructs an [[InputOntologyV2]] based on a JSON-LD document. - * - * @param jsonLDDocument a JSON-LD document representing information about the ontology. - * @param parsingMode the parsing mode to be used. - * @return an [[InputOntologyV2]] representing the same information. - */ - def fromJsonLD(jsonLDDocument: JsonLDDocument, - parsingMode: InputOntologyParsingModeV2 = ClientInputParsingModeV2): InputOntologyV2 = { + * Constructs an [[InputOntologyV2]] based on a JSON-LD document. + * + * @param jsonLDDocument a JSON-LD document representing information about the ontology. + * @param parsingMode the parsing mode to be used. + * @return an [[InputOntologyV2]] representing the same information. + */ + def fromJsonLD( + jsonLDDocument: JsonLDDocument, + parsingMode: InputOntologyParsingModeV2 = ClientInputParsingModeV2 + ): InputOntologyV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val ontologyObj: JsonLDObject = jsonLDDocument.body @@ -1728,7 +1938,8 @@ object InputOntologyV2 { val projectIri: Option[SmartIri] = ontologyObj.maybeIriInObject( OntologyConstants.KnoraApiV2Complex.AttachedToProject, - stringFormatter.toSmartIriWithErr) + stringFormatter.toSmartIriWithErr + ) val ontologyLabel: Option[String] = ontologyObj.maybeStringWithValidation(OntologyConstants.Rdfs.Label, stringFormatter.toSparqlEncodedString) @@ -1793,7 +2004,8 @@ object InputOntologyV2 { if (entityIrisInWrongOntology.nonEmpty) { throw BadRequestException( - s"One or more entities are not in ontology $externalOntologyIri: ${entityIrisInWrongOntology.mkString(", ")}") + s"One or more entities are not in ontology $externalOntologyIri: ${entityIrisInWrongOntology.mkString(", ")}" + ) } InputOntologyV2( @@ -1811,10 +2023,10 @@ object InputOntologyV2 { } /** - * Returns metadata about Knora ontologies. - * - * @param ontologies the metadata to be returned. - */ + * Returns metadata about Knora ontologies. + * + * @param ontologies the metadata to be returned. + */ case class ReadOntologyMetadataV2(ontologies: Set[OntologyMetadataV2]) extends KnoraJsonLDResponseV2 with KnoraReadV2[ReadOntologyMetadataV2] { @@ -1841,10 +2053,11 @@ case class ReadOntologyMetadataV2(ontologies: Set[OntologyMetadataV2]) val context = JsonLDObject( Map( OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> JsonLDString(knoraApiOntologyPrefixExpansion), - "xsd" -> JsonLDString(OntologyConstants.Xsd.XsdPrefixExpansion), - "rdfs" -> JsonLDString(OntologyConstants.Rdfs.RdfsPrefixExpansion), - "owl" -> JsonLDString(OntologyConstants.Owl.OwlPrefixExpansion) - )) + "xsd" -> JsonLDString(OntologyConstants.Xsd.XsdPrefixExpansion), + "rdfs" -> JsonLDString(OntologyConstants.Rdfs.RdfsPrefixExpansion), + "owl" -> JsonLDString(OntologyConstants.Owl.OwlPrefixExpansion) + ) + ) val ontologiesJson: Vector[JsonLDObject] = ontologies.toVector.sortBy(_.ontologyIri).map(ontology => JsonLDObject(ontology.toJsonLD(targetSchema))) @@ -1852,34 +2065,36 @@ case class ReadOntologyMetadataV2(ontologies: Set[OntologyMetadataV2]) val body = JsonLDObject( Map( JsonLDKeywords.GRAPH -> JsonLDArray(ontologiesJson) - )) + ) + ) JsonLDDocument(body = body, context = context, isFlat = true) } - def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = toOntologySchema(targetSchema).generateJsonLD(targetSchema) - } } /** - * Represents a predicate that is asserted about a given ontology entity, and the objects of that predicate. - * - * @param predicateIri the IRI of the predicate. - * @param objects the objects of the predicate. - */ + * Represents a predicate that is asserted about a given ontology entity, and the objects of that predicate. + * + * @param predicateIri the IRI of the predicate. + * @param objects the objects of the predicate. + */ case class PredicateInfoV2(predicateIri: SmartIri, objects: Seq[OntologyLiteralV2] = Seq.empty[OntologyLiteralV2]) { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * Converts this [[PredicateInfoV2]] to another ontology schema. - * - * @param targetSchema the target schema. - * @return the converted [[PredicateInfoV2]]. - */ - def toOntologySchema(targetSchema: OntologySchema): PredicateInfoV2 = { + * Converts this [[PredicateInfoV2]] to another ontology schema. + * + * @param targetSchema the target schema. + * @return the converted [[PredicateInfoV2]]. + */ + def toOntologySchema(targetSchema: OntologySchema): PredicateInfoV2 = copy( predicateIri = predicateIri.toOntologySchema(targetSchema), objects = objects.map { @@ -1887,29 +2102,27 @@ case class PredicateInfoV2(predicateIri: SmartIri, objects: Seq[OntologyLiteralV case other => other } ) - } /** - * Requires this predicate to have a single IRI object, and returns that object. - * - * @param errorFun a function that throws an error. It will be called if the predicate does not have a single - * IRI object. - * @return the predicate's IRI object. - */ - def requireIriObject(errorFun: => Nothing): SmartIri = { + * Requires this predicate to have a single IRI object, and returns that object. + * + * @param errorFun a function that throws an error. It will be called if the predicate does not have a single + * IRI object. + * @return the predicate's IRI object. + */ + def requireIriObject(errorFun: => Nothing): SmartIri = objects match { case Seq(SmartIriLiteralV2(iri)) => iri case _ => errorFun } - } /** - * Requires this predicate to have at least one IRI, and returns those objects. - * - * @param errorFun a function that throws an error. It will be called if the predicate has no objects, - * or has non-IRI objects. - * @return the predicate's IRI objects. - */ + * Requires this predicate to have at least one IRI, and returns those objects. + * + * @param errorFun a function that throws an error. It will be called if the predicate has no objects, + * or has non-IRI objects. + * @return the predicate's IRI objects. + */ def requireIriObjects(errorFun: => Nothing): Set[SmartIri] = { if (objects.isEmpty) { errorFun @@ -1922,27 +2135,25 @@ case class PredicateInfoV2(predicateIri: SmartIri, objects: Seq[OntologyLiteralV } /** - * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used after an update, when the - * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data - * read back from the triplestore (in which predicate objects are not escaped). - * - * @return this predicate with its objects unescaped. - */ - def unescape: PredicateInfoV2 = { + * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used after an update, when the + * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data + * read back from the triplestore (in which predicate objects are not escaped). + * + * @return this predicate with its objects unescaped. + */ + def unescape: PredicateInfoV2 = copy( objects = objects.map { case StringLiteralV2(str, lang) => StringLiteralV2(stringFormatter.fromSparqlEncodedString(str), lang) case other => other } ) - } - override def hashCode(): Int = { + override def hashCode(): Int = // Ignore the order of predicate objects when generating hash codes for this class. new HashCodeBuilder(17, 37).append(predicateIri).append(objects.toSet).toHashCode - } - override def equals(that: scala.Any): Boolean = { + override def equals(that: scala.Any): Boolean = // Ignore the order of predicate objects when testing equality for this class. that match { case otherPred: PredicateInfoV2 => @@ -1951,23 +2162,22 @@ case class PredicateInfoV2(predicateIri: SmartIri, objects: Seq[OntologyLiteralV case _ => false } - } } /** - * Represents the OWL cardinalities that Knora supports. - */ + * Represents the OWL cardinalities that Knora supports. + */ object Cardinality extends Enumeration { /** - * Represents information about an OWL cardinality. - * - * @param owlCardinalityIri the IRI of the OWL cardinality, which must be a member of the set - * [[OntologyConstants.Owl.cardinalityOWLRestrictions]]. - * @param owlCardinalityValue the value of the OWL cardinality, which must be 0 or 1. - * @param guiOrder the SALSAH GUI order. - * @return a [[Value]]. - */ + * Represents information about an OWL cardinality. + * + * @param owlCardinalityIri the IRI of the OWL cardinality, which must be a member of the set + * [[OntologyConstants.Owl.cardinalityOWLRestrictions]]. + * @param owlCardinalityValue the value of the OWL cardinality, which must be 0 or 1. + * @param guiOrder the SALSAH GUI order. + * @return a [[Value]]. + */ case class OwlCardinalityInfo(owlCardinalityIri: IRI, owlCardinalityValue: Int, guiOrder: Option[Int] = None) { if (!OntologyConstants.Owl.cardinalityOWLRestrictions.contains(owlCardinalityIri)) { throw InconsistentRepositoryDataException(s"Invalid OWL cardinality property: $owlCardinalityIri") @@ -1979,79 +2189,81 @@ object Cardinality extends Enumeration { override def toString: String = s"<$owlCardinalityIri> $owlCardinalityValue" - def equalsWithoutGuiOrder(that: OwlCardinalityInfo): Boolean = { + def equalsWithoutGuiOrder(that: OwlCardinalityInfo): Boolean = owlCardinalityIri == that.owlCardinalityIri && owlCardinalityValue == that.owlCardinalityValue - } } /** - * Represents a Knora cardinality with an optional SALSAH GUI order. - * - * @param cardinality the Knora cardinality. - * @param guiOrder the SALSAH GUI order. - */ + * Represents a Knora cardinality with an optional SALSAH GUI order. + * + * @param cardinality the Knora cardinality. + * @param guiOrder the SALSAH GUI order. + */ case class KnoraCardinalityInfo(cardinality: Value, guiOrder: Option[Int] = None) { override def toString: String = guiOrder match { case Some(definedGuiOrder) => s"$cardinality (guiOrder $definedGuiOrder)" case None => cardinality.toString } - def equalsWithoutGuiOrder(that: KnoraCardinalityInfo): Boolean = { + def equalsWithoutGuiOrder(that: KnoraCardinalityInfo): Boolean = that.cardinality == cardinality - } } type Cardinality = Value - val MayHaveOne: Value = Value(0, "0-1") - val MayHaveMany: Value = Value(1, "0-n") - val MustHaveOne: Value = Value(2, "1") + val MayHaveOne: Value = Value(0, "0-1") + val MayHaveMany: Value = Value(1, "0-n") + val MustHaveOne: Value = Value(2, "1") val MustHaveSome: Value = Value(3, "1-n") val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap /** - * The valid mappings between Knora cardinalities and OWL cardinalities. - */ + * The valid mappings between Knora cardinalities and OWL cardinalities. + */ private val knoraCardinality2OwlCardinalityMap: Map[Value, OwlCardinalityInfo] = Map( MayHaveOne -> OwlCardinalityInfo(owlCardinalityIri = OntologyConstants.Owl.MaxCardinality, owlCardinalityValue = 1), - MayHaveMany -> OwlCardinalityInfo(owlCardinalityIri = OntologyConstants.Owl.MinCardinality, - owlCardinalityValue = 0), + MayHaveMany -> OwlCardinalityInfo( + owlCardinalityIri = OntologyConstants.Owl.MinCardinality, + owlCardinalityValue = 0 + ), MustHaveOne -> OwlCardinalityInfo(owlCardinalityIri = OntologyConstants.Owl.Cardinality, owlCardinalityValue = 1), - MustHaveSome -> OwlCardinalityInfo(owlCardinalityIri = OntologyConstants.Owl.MinCardinality, - owlCardinalityValue = 1) + MustHaveSome -> OwlCardinalityInfo( + owlCardinalityIri = OntologyConstants.Owl.MinCardinality, + owlCardinalityValue = 1 + ) ) private val owlCardinality2KnoraCardinalityMap: Map[OwlCardinalityInfo, Value] = - knoraCardinality2OwlCardinalityMap.map { - case (knoraC, owlC) => (owlC, knoraC) + knoraCardinality2OwlCardinalityMap.map { case (knoraC, owlC) => + (owlC, knoraC) } /** - * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an - * [[InconsistentRepositoryDataException]]. - * - * @param name the name of the value. - * @return the requested value. - */ - def lookup(name: String): Value = { + * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an + * [[InconsistentRepositoryDataException]]. + * + * @param name the name of the value. + * @return the requested value. + */ + def lookup(name: String): Value = valueMap.get(name) match { case Some(value) => value case None => throw InconsistentRepositoryDataException(s"Cardinality not found: $name") } - } /** - * Converts information about an OWL cardinality restriction to a [[Value]] of this enumeration. - * - * @param propertyIri the IRI of the property that the OWL cardinality applies to. - * @param owlCardinality information about an OWL cardinality. - * @return a [[Value]]. - */ + * Converts information about an OWL cardinality restriction to a [[Value]] of this enumeration. + * + * @param propertyIri the IRI of the property that the OWL cardinality applies to. + * @param owlCardinality information about an OWL cardinality. + * @return a [[Value]]. + */ def owlCardinality2KnoraCardinality(propertyIri: IRI, owlCardinality: OwlCardinalityInfo): KnoraCardinalityInfo = { val cardinality = owlCardinality2KnoraCardinalityMap.getOrElse( owlCardinality.copy(guiOrder = None), - throw InconsistentRepositoryDataException(s"Invalid OWL cardinality $owlCardinality for $propertyIri")) + throw InconsistentRepositoryDataException(s"Invalid OWL cardinality $owlCardinality for $propertyIri") + ) KnoraCardinalityInfo( cardinality = cardinality, @@ -2060,25 +2272,24 @@ object Cardinality extends Enumeration { } /** - * Converts a [[Value]] of this enumeration to information about an OWL cardinality restriction. - * - * @param knoraCardinality a [[Value]]. - * @return an [[OwlCardinalityInfo]]. - */ - def knoraCardinality2OwlCardinality(knoraCardinality: KnoraCardinalityInfo): OwlCardinalityInfo = { + * Converts a [[Value]] of this enumeration to information about an OWL cardinality restriction. + * + * @param knoraCardinality a [[Value]]. + * @return an [[OwlCardinalityInfo]]. + */ + def knoraCardinality2OwlCardinality(knoraCardinality: KnoraCardinalityInfo): OwlCardinalityInfo = knoraCardinality2OwlCardinalityMap(knoraCardinality.cardinality).copy(guiOrder = knoraCardinality.guiOrder) - } /** - * Checks whether a cardinality that is directly defined on a class is compatible with an inherited cardinality on the - * same property. This will be true only if the directly defined cardinality is at least as restrictive as the - * inherited one. - * - * @param directCardinality the directly defined cardinality. - * @param inheritableCardinality the inherited cardinality. - * @return `true` if the directly defined cardinality is compatible with the inherited one. - */ - def isCompatible(directCardinality: Value, inheritableCardinality: Value): Boolean = { + * Checks whether a cardinality that is directly defined on a class is compatible with an inherited cardinality on the + * same property. This will be true only if the directly defined cardinality is at least as restrictive as the + * inherited one. + * + * @param directCardinality the directly defined cardinality. + * @param inheritableCardinality the inherited cardinality. + * @return `true` if the directly defined cardinality is compatible with the inherited one. + */ + def isCompatible(directCardinality: Value, inheritableCardinality: Value): Boolean = if (directCardinality == inheritableCardinality) { true } else { @@ -2089,107 +2300,106 @@ object Cardinality extends Enumeration { case MustHaveSome => directCardinality == MustHaveOne } } - } } /** - * Represents information about an ontology entity. - */ + * Represents information about an ontology entity. + */ sealed trait EntityInfoContentV2 { /** - * The predicates of the entity, and their objects. - */ + * The predicates of the entity, and their objects. + */ val predicates: Map[SmartIri, PredicateInfoV2] protected implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * Checks that a predicate is present in this [[EntityInfoContentV2]] and that it has a single IRI object, and - * returns the object as a [[SmartIri]]. - * - * @param predicateIri the IRI of the predicate. - * @param errorFun a function that will be called if the predicate is absent or if its object is not an IRI. - * @return a [[SmartIri]] representing the predicate's object. - */ - def requireIriObject(predicateIri: SmartIri, errorFun: => Nothing): SmartIri = { + * Checks that a predicate is present in this [[EntityInfoContentV2]] and that it has a single IRI object, and + * returns the object as a [[SmartIri]]. + * + * @param predicateIri the IRI of the predicate. + * @param errorFun a function that will be called if the predicate is absent or if its object is not an IRI. + * @return a [[SmartIri]] representing the predicate's object. + */ + def requireIriObject(predicateIri: SmartIri, errorFun: => Nothing): SmartIri = predicates.getOrElse(predicateIri, errorFun).requireIriObject(errorFun) - } /** - * Checks that a predicate is present in this [[EntityInfoContentV2]] and that it at least one IRI object, and - * returns those objects as a set of [[SmartIri]] instances. - * - * @param predicateIri the IRI of the predicate. - * @param errorFun a function that will be called if the predicate is absent or if its objects are not IRIs. - * @return a set of [[SmartIri]] instances representing the predicate's objects. - */ - def requireIriObjects(predicateIri: SmartIri, errorFun: => Nothing): Set[SmartIri] = { + * Checks that a predicate is present in this [[EntityInfoContentV2]] and that it at least one IRI object, and + * returns those objects as a set of [[SmartIri]] instances. + * + * @param predicateIri the IRI of the predicate. + * @param errorFun a function that will be called if the predicate is absent or if its objects are not IRIs. + * @return a set of [[SmartIri]] instances representing the predicate's objects. + */ + def requireIriObjects(predicateIri: SmartIri, errorFun: => Nothing): Set[SmartIri] = predicates.getOrElse(predicateIri, errorFun).requireIriObjects(errorFun) - } /** - * A convenience method that returns the canonical `rdf:type` of this entity. Throws [[InconsistentRepositoryDataException]] - * if the entity's predicates do not include `rdf:type`. - * - * @return the entity's `rdf:type`. - */ + * A convenience method that returns the canonical `rdf:type` of this entity. Throws [[InconsistentRepositoryDataException]] + * if the entity's predicates do not include `rdf:type`. + * + * @return the entity's `rdf:type`. + */ def getRdfType: SmartIri /** - * A convenience method that returns all the objects of this entity's `rdf:type` predicate. Throws [[InconsistentRepositoryDataException]] - * * if the entity's predicates do not include `rdf:type`. - * - * @return all the values of `rdf:type` for this entity, sorted for determinism. - */ + * A convenience method that returns all the objects of this entity's `rdf:type` predicate. Throws [[InconsistentRepositoryDataException]] + * * if the entity's predicates do not include `rdf:type`. + * + * @return all the values of `rdf:type` for this entity, sorted for determinism. + */ def getRdfTypes: Seq[SmartIri] /** - * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used after an update, when the - * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data - * read back from the triplestore (in which predicate objects are not escaped). - * - * @return the predicates of this [[EntityInfoContentV2]], with their objects unescaped. - */ - protected def unescapePredicateObjects: Map[SmartIri, PredicateInfoV2] = { - predicates.map { - case (predicateIri, predicateInfo) => predicateIri -> predicateInfo.unescape + * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used after an update, when the + * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data + * read back from the triplestore (in which predicate objects are not escaped). + * + * @return the predicates of this [[EntityInfoContentV2]], with their objects unescaped. + */ + protected def unescapePredicateObjects: Map[SmartIri, PredicateInfoV2] = + predicates.map { case (predicateIri, predicateInfo) => + predicateIri -> predicateInfo.unescape } - } /** - * Gets a predicate and its object from an entity in a specific language. - * - * @param predicateIri the IRI of the predicate. - * @param userLang the language in which the object should to be returned. - * @return the requested predicate and object. - */ - def getPredicateAndStringLiteralObjectWithLang(predicateIri: SmartIri, - settings: KnoraSettingsImpl, - userLang: String): Option[(SmartIri, String)] = { + * Gets a predicate and its object from an entity in a specific language. + * + * @param predicateIri the IRI of the predicate. + * @param userLang the language in which the object should to be returned. + * @return the requested predicate and object. + */ + def getPredicateAndStringLiteralObjectWithLang( + predicateIri: SmartIri, + settings: KnoraSettingsImpl, + userLang: String + ): Option[(SmartIri, String)] = getPredicateStringLiteralObject( predicateIri = predicateIri, preferredLangs = Some(userLang, settings.fallbackLanguage) ).map(obj => predicateIri -> obj) - } /** - * Returns an object for a given predicate. If requested, attempts to return the object in the user's preferred - * language, in the system's default language, or in any language, in that order. - * - * @param predicateIri the IRI of the predicate. - * @param preferredLangs the user's preferred language and the system's default language. - * @return an object for the predicate, or [[None]] if this entity doesn't have the specified predicate, or - * if the predicate has no objects. - */ - def getPredicateStringLiteralObject(predicateIri: SmartIri, - preferredLangs: Option[(String, String)] = None): Option[String] = { + * Returns an object for a given predicate. If requested, attempts to return the object in the user's preferred + * language, in the system's default language, or in any language, in that order. + * + * @param predicateIri the IRI of the predicate. + * @param preferredLangs the user's preferred language and the system's default language. + * @return an object for the predicate, or [[None]] if this entity doesn't have the specified predicate, or + * if the predicate has no objects. + */ + def getPredicateStringLiteralObject( + predicateIri: SmartIri, + preferredLangs: Option[(String, String)] = None + ): Option[String] = // Does the predicate exist? predicates.get(predicateIri) match { case Some(predicateInfo) => // Yes. Make a sequence of its string values. - val stringLiterals: Vector[StringLiteralV2] = predicateInfo.objects.collect { - case strLit: StringLiteralV2 => strLit + val stringLiterals: Vector[StringLiteralV2] = predicateInfo.objects.collect { case strLit: StringLiteralV2 => + strLit }.toVector val stringLiteralSequence = StringLiteralSequenceV2(stringLiterals) @@ -2210,54 +2420,51 @@ sealed trait EntityInfoContentV2 { case None => None } - } /** - * Returns all the non-language-specific string objects specified for a given predicate. - * - * @param predicateIri the IRI of the predicate. - * @return the predicate's objects, or an empty set if this entity doesn't have the specified predicate. - */ - def getPredicateStringLiteralObjectsWithoutLang(predicateIri: SmartIri): Seq[String] = { + * Returns all the non-language-specific string objects specified for a given predicate. + * + * @param predicateIri the IRI of the predicate. + * @return the predicate's objects, or an empty set if this entity doesn't have the specified predicate. + */ + def getPredicateStringLiteralObjectsWithoutLang(predicateIri: SmartIri): Seq[String] = predicates.get(predicateIri) match { case Some(predicateInfo) => - predicateInfo.objects.collect { - case StringLiteralV2(str, None) => str + predicateInfo.objects.collect { case StringLiteralV2(str, None) => + str } case None => Seq.empty[String] } - } /** - * Returns all the IRI objects specified for a given predicate. - * - * @param predicateIri the IRI of the predicate. - * @return the predicate's IRI objects, or an empty set if this entity doesn't have the specified predicate. - */ - def getPredicateIriObjects(predicateIri: SmartIri): Seq[SmartIri] = { + * Returns all the IRI objects specified for a given predicate. + * + * @param predicateIri the IRI of the predicate. + * @return the predicate's IRI objects, or an empty set if this entity doesn't have the specified predicate. + */ + def getPredicateIriObjects(predicateIri: SmartIri): Seq[SmartIri] = predicates.get(predicateIri) match { case Some(predicateInfo) => - predicateInfo.objects.collect { - case SmartIriLiteralV2(iri) => iri + predicateInfo.objects.collect { case SmartIriLiteralV2(iri) => + iri } case None => Seq.empty[SmartIri] } - } /** - * Returns the first object specified as a boolean value for the given predicate, or `false` if the - * entity doesn't have that predicate. - * - * @param predicateIri the IRI of the predicate. - * @return the predicate's object, if given, otherwise `false`. - */ + * Returns the first object specified as a boolean value for the given predicate, or `false` if the + * entity doesn't have that predicate. + * + * @param predicateIri the IRI of the predicate. + * @return the predicate's object, if given, otherwise `false`. + */ def getPredicateBooleanObject(predicateIri: SmartIri): Boolean = { val values: Seq[Boolean] = predicates.get(predicateIri) match { case Some(predicateInfo) => - predicateInfo.objects.collect { - case BooleanLiteralV2(value) => value + predicateInfo.objects.collect { case BooleanLiteralV2(value) => + value } case None => Seq.empty[Boolean] @@ -2271,49 +2478,49 @@ sealed trait EntityInfoContentV2 { } /** - * Returns the first object specified as an IRI for the given predicate. - * - * @param predicateIri the IRI of the predicate. - * @return the predicate's object, if given. - */ + * Returns the first object specified as an IRI for the given predicate. + * + * @param predicateIri the IRI of the predicate. + * @return the predicate's object, if given. + */ def getPredicateIriObject(predicateIri: SmartIri): Option[SmartIri] = getPredicateIriObjects(predicateIri).headOption /** - * Returns all the objects specified for a given predicate, along with the language tag of each object. - * - * @param predicateIri the IRI of the predicate. - * @return a map of language tags to objects, or an empty map if this entity doesn't have the specified predicate. - */ - def getPredicateObjectsWithLangs(predicateIri: SmartIri): Map[String, String] = { + * Returns all the objects specified for a given predicate, along with the language tag of each object. + * + * @param predicateIri the IRI of the predicate. + * @return a map of language tags to objects, or an empty map if this entity doesn't have the specified predicate. + */ + def getPredicateObjectsWithLangs(predicateIri: SmartIri): Map[String, String] = predicates.get(predicateIri) match { case Some(predicateInfo) => - predicateInfo.objects.collect { - case StringLiteralV2(str, Some(lang)) => lang -> str + predicateInfo.objects.collect { case StringLiteralV2(str, Some(lang)) => + lang -> str }.toMap case None => Map.empty[String, String] } - } } /** - * Processes predicates from a JSON-LD class or property definition. - */ + * Processes predicates from a JSON-LD class or property definition. + */ object EntityInfoContentV2 { private def stringToLiteral(str: String): StringLiteralV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance StringLiteralV2( - stringFormatter.toSparqlEncodedString(str, throw BadRequestException(s"Invalid predicate object: $str"))) + stringFormatter.toSparqlEncodedString(str, throw BadRequestException(s"Invalid predicate object: $str")) + ) } /** - * Processes predicates from a JSON-LD class or property definition. Converts `@type` to `rdf:type`. Ignores - * `\@id`, `rdfs:subClassOf` and `rdfs:subPropertyOf`. - * - * @param jsonLDObject the JSON-LD class or property definition. - * @return a map of predicate IRIs to [[PredicateInfoV2]] objects. - */ + * Processes predicates from a JSON-LD class or property definition. Converts `@type` to `rdf:type`. Ignores + * `\@id`, `rdfs:subClassOf` and `rdfs:subPropertyOf`. + * + * @param jsonLDObject the JSON-LD class or property definition. + * @return a map of predicate IRIs to [[PredicateInfoV2]] objects. + */ def predicatesFromJsonLDObject(jsonLDObject: JsonLDObject): Map[SmartIri, PredicateInfoV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -2325,144 +2532,150 @@ object EntityInfoContentV2 { objects = Seq(SmartIriLiteralV2(entityType)) ) - val predicates = jsonLDObject.value - JsonLDKeywords.ID - JsonLDKeywords.TYPE - OntologyConstants.Rdfs.SubClassOf - OntologyConstants.Rdfs.SubPropertyOf - OntologyConstants.Owl.WithRestrictions + val predicates = + jsonLDObject.value - JsonLDKeywords.ID - JsonLDKeywords.TYPE - OntologyConstants.Rdfs.SubClassOf - OntologyConstants.Rdfs.SubPropertyOf - OntologyConstants.Owl.WithRestrictions + + predicates.map { case (predicateIriStr: IRI, predicateValue: JsonLDValue) => + val predicateIri = predicateIriStr.toSmartIri - predicates.map { - case (predicateIriStr: IRI, predicateValue: JsonLDValue) => - val predicateIri = predicateIriStr.toSmartIri + val predicateInfo: PredicateInfoV2 = predicateValue match { + case JsonLDString(objStr) => + PredicateInfoV2( + predicateIri = predicateIri, + objects = Seq(stringToLiteral(objStr)) + ) + + case JsonLDBoolean(objBoolean) => + PredicateInfoV2( + predicateIri = predicateIri, + objects = Seq(BooleanLiteralV2(objBoolean)) + ) - val predicateInfo: PredicateInfoV2 = predicateValue match { - case JsonLDString(objStr) => + case objObj: JsonLDObject => + if (objObj.isIri) { + // This is a JSON-LD IRI value. PredicateInfoV2( predicateIri = predicateIri, - objects = Seq(stringToLiteral(objStr)) + objects = Seq(SmartIriLiteralV2(objObj.toIri(stringFormatter.toSmartIriWithErr))) ) - - case JsonLDBoolean(objBoolean) => + } else if (objObj.isStringWithLang) { + // This is a string with a language tag. PredicateInfoV2( predicateIri = predicateIri, - objects = Seq(BooleanLiteralV2(objBoolean)) + objects = JsonLDArray(Seq(objObj)).toObjsWithLang ) + } else { + throw BadRequestException(s"Unexpected object value for predicate $predicateIri: $objObj") + } - case objObj: JsonLDObject => - if (objObj.isIri) { - // This is a JSON-LD IRI value. - PredicateInfoV2( - predicateIri = predicateIri, - objects = Seq(SmartIriLiteralV2(objObj.toIri(stringFormatter.toSmartIriWithErr))) - ) - } else if (objObj.isStringWithLang) { - // This is a string with a language tag. - PredicateInfoV2( - predicateIri = predicateIri, - objects = JsonLDArray(Seq(objObj)).toObjsWithLang - ) - } else { - throw BadRequestException(s"Unexpected object value for predicate $predicateIri: $objObj") - } + case objArray: JsonLDArray => + if (objArray.value.isEmpty) { + throw BadRequestException(s"No values provided for predicate $predicateIri") + } - case objArray: JsonLDArray => - if (objArray.value.isEmpty) { - throw BadRequestException(s"No values provided for predicate $predicateIri") + if (objArray.value.forall(_.isInstanceOf[JsonLDString])) { + // All the elements of the array are strings. + PredicateInfoV2( + predicateIri = predicateIri, + objects = objArray.value.map { + case JsonLDString(objStr) => stringToLiteral(objStr) + case other => throw AssertionException(s"Invalid object for predicate $predicateIriStr: $other") + } + ) + } else if ( + objArray.value.forall { + case jsonObjElem: JsonLDObject if jsonObjElem.isIri => + // All the elements of the array are IRI values. + true + case _ => false } - - if (objArray.value.forall(_.isInstanceOf[JsonLDString])) { - // All the elements of the array are strings. - PredicateInfoV2( - predicateIri = predicateIri, - objects = objArray.value.map { - case JsonLDString(objStr) => stringToLiteral(objStr) - case other => throw AssertionException(s"Invalid object for predicate $predicateIriStr: $other") - } - ) - } else if (objArray.value.forall { - case jsonObjElem: JsonLDObject if jsonObjElem.isIri => - // All the elements of the array are IRI values. - true - case _ => false - }) { - PredicateInfoV2( - predicateIri = predicateIri, - objects = objArray.value.map { - case jsonObjElem: JsonLDObject => - SmartIriLiteralV2(jsonObjElem.toIri(stringFormatter.toSmartIriWithErr)) - case other => throw AssertionException(s"Invalid object for predicate $predicateIriStr: $other") - } - ) - } else if (objArray.value.forall { - case jsonObjElem: JsonLDObject if jsonObjElem.isStringWithLang => - // All the elements of the array are strings with language codes. - true - case _ => false - }) { - PredicateInfoV2( - predicateIri = predicateIri, - objects = objArray.toObjsWithLang - ) - } else { - throw BadRequestException(s"Invalid object for predicate $predicateIriStr: $predicateValue") + ) { + PredicateInfoV2( + predicateIri = predicateIri, + objects = objArray.value.map { + case jsonObjElem: JsonLDObject => + SmartIriLiteralV2(jsonObjElem.toIri(stringFormatter.toSmartIriWithErr)) + case other => throw AssertionException(s"Invalid object for predicate $predicateIriStr: $other") + } + ) + } else if ( + objArray.value.forall { + case jsonObjElem: JsonLDObject if jsonObjElem.isStringWithLang => + // All the elements of the array are strings with language codes. + true + case _ => false } + ) { + PredicateInfoV2( + predicateIri = predicateIri, + objects = objArray.toObjsWithLang + ) + } else { + throw BadRequestException(s"Invalid object for predicate $predicateIriStr: $predicateValue") + } - case other => throw BadRequestException(s"Invalid object for predicate $predicateIriStr: $other") - } + case other => throw BadRequestException(s"Invalid object for predicate $predicateIriStr: $other") + } - predicateIri -> predicateInfo + predicateIri -> predicateInfo } + rdfType } } /** - * Represents information about an ontolgoy entity, as returned in an API response. - */ + * Represents information about an ontolgoy entity, as returned in an API response. + */ sealed trait ReadEntityInfoV2 { /** - * Provides basic information about the entity. - */ + * Provides basic information about the entity. + */ val entityInfoContent: EntityInfoContentV2 protected implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * Returns the contents of a JSON-LD object containing non-language-specific information about the entity. - * - * @param targetSchema the API v2 schema in which the response will be returned. - */ + * Returns the contents of a JSON-LD object containing non-language-specific information about the entity. + * + * @param targetSchema the API v2 schema in which the response will be returned. + */ protected def getNonLanguageSpecific(targetSchema: ApiV2Schema): Map[IRI, JsonLDValue] /** - * Returns a JSON-LD object representing the entity, with language-specific information provided in a single language. - * - * @param targetSchema the API v2 schema in which the response will be returned. - * @param userLang the user's preferred language. - * @param settings the application settings. - * @return a JSON-LD object representing the entity. - */ - def toJsonLDWithSingleLanguage(targetSchema: ApiV2Schema, - userLang: String, - settings: KnoraSettingsImpl): JsonLDObject = { + * Returns a JSON-LD object representing the entity, with language-specific information provided in a single language. + * + * @param targetSchema the API v2 schema in which the response will be returned. + * @param userLang the user's preferred language. + * @param settings the application settings. + * @return a JSON-LD object representing the entity. + */ + def toJsonLDWithSingleLanguage( + targetSchema: ApiV2Schema, + userLang: String, + settings: KnoraSettingsImpl + ): JsonLDObject = { val label: Option[(IRI, JsonLDString)] = entityInfoContent .getPredicateAndStringLiteralObjectWithLang(OntologyConstants.Rdfs.Label.toSmartIri, settings, userLang) - .map { - case (k: SmartIri, v: String) => (k.toString, JsonLDString(v)) + .map { case (k: SmartIri, v: String) => + (k.toString, JsonLDString(v)) } val comment: Option[(IRI, JsonLDString)] = entityInfoContent .getPredicateAndStringLiteralObjectWithLang(OntologyConstants.Rdfs.Comment.toSmartIri, settings, userLang) - .map { - case (k: SmartIri, v: String) => (k.toString, JsonLDString(v)) + .map { case (k: SmartIri, v: String) => + (k.toString, JsonLDString(v)) } JsonLDObject(getNonLanguageSpecific(targetSchema) ++ label ++ comment) } /** - * Returns a JSON-LD object representing the entity, with language-specific information provided in all - * available languages. - * - * @param targetSchema the API v2 schema in which the response will be returned. - * @return a JSON-LD object representing the entity. - */ + * Returns a JSON-LD object representing the entity, with language-specific information provided in all + * available languages. + * + * @param targetSchema the API v2 schema in which the response will be returned. + * @return a JSON-LD object representing the entity. + */ def toJsonLDWithAllLanguages(targetSchema: ApiV2Schema): JsonLDObject = { val labelObjs: Map[String, String] = entityInfoContent.getPredicateObjectsWithLangs(OntologyConstants.Rdfs.Label.toSmartIri) @@ -2487,48 +2700,48 @@ sealed trait ReadEntityInfoV2 { } /** - * Represents an OWL class definition as returned in an API response. - * - * @param entityInfoContent a [[ReadClassInfoV2]] providing information about the class. - * @param allBaseClasses a seq of the IRIs of all the base classes of the class. - * @param isResourceClass `true` if this is a subclass of `knora-base:Resource`. - * @param isStandoffClass `true` if this is a subclass of `knora-base:StandoffTag`. - * @param isValueClass `true` if the class is a Knora value class. - * @param canBeInstantiated `true` if the class is a Knora resource class that can be instantiated via the API. - * @param inheritedCardinalities a [[Map]] of properties to [[Cardinality.Value]] objects representing the class's - * inherited cardinalities on those properties. - * @param standoffDataType if this is a standoff tag class, the standoff datatype tag class (if any) that it - * is a subclass of. - * @param knoraResourceProperties a [[Set]] of IRIs of properties in `allCardinalities` that are subproperties of `knora-base:resourceProperty`. - * @param linkProperties a [[Set]] of IRIs of properties in `allCardinalities` that point to resources. - * @param linkValueProperties a [[Set]] of IRIs of properties in `allCardinalities` that point to `LinkValue` objects. - * @param fileValueProperties a [[Set]] of IRIs of properties in `allCardinalities` that point to `FileValue` objects. - */ -case class ReadClassInfoV2(entityInfoContent: ClassInfoContentV2, - allBaseClasses: Seq[SmartIri], - isResourceClass: Boolean = false, - isStandoffClass: Boolean = false, - isValueClass: Boolean = false, - canBeInstantiated: Boolean = false, - inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = - Map.empty[SmartIri, KnoraCardinalityInfo], - standoffDataType: Option[StandoffDataTypeClasses.Value] = None, - knoraResourceProperties: Set[SmartIri] = Set.empty[SmartIri], - linkProperties: Set[SmartIri] = Set.empty[SmartIri], - linkValueProperties: Set[SmartIri] = Set.empty[SmartIri], - fileValueProperties: Set[SmartIri] = Set.empty[SmartIri]) - extends ReadEntityInfoV2 + * Represents an OWL class definition as returned in an API response. + * + * @param entityInfoContent a [[ReadClassInfoV2]] providing information about the class. + * @param allBaseClasses a seq of the IRIs of all the base classes of the class. + * @param isResourceClass `true` if this is a subclass of `knora-base:Resource`. + * @param isStandoffClass `true` if this is a subclass of `knora-base:StandoffTag`. + * @param isValueClass `true` if the class is a Knora value class. + * @param canBeInstantiated `true` if the class is a Knora resource class that can be instantiated via the API. + * @param inheritedCardinalities a [[Map]] of properties to [[Cardinality.Value]] objects representing the class's + * inherited cardinalities on those properties. + * @param standoffDataType if this is a standoff tag class, the standoff datatype tag class (if any) that it + * is a subclass of. + * @param knoraResourceProperties a [[Set]] of IRIs of properties in `allCardinalities` that are subproperties of `knora-base:resourceProperty`. + * @param linkProperties a [[Set]] of IRIs of properties in `allCardinalities` that point to resources. + * @param linkValueProperties a [[Set]] of IRIs of properties in `allCardinalities` that point to `LinkValue` objects. + * @param fileValueProperties a [[Set]] of IRIs of properties in `allCardinalities` that point to `FileValue` objects. + */ +case class ReadClassInfoV2( + entityInfoContent: ClassInfoContentV2, + allBaseClasses: Seq[SmartIri], + isResourceClass: Boolean = false, + isStandoffClass: Boolean = false, + isValueClass: Boolean = false, + canBeInstantiated: Boolean = false, + inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = Map.empty[SmartIri, KnoraCardinalityInfo], + standoffDataType: Option[StandoffDataTypeClasses.Value] = None, + knoraResourceProperties: Set[SmartIri] = Set.empty[SmartIri], + linkProperties: Set[SmartIri] = Set.empty[SmartIri], + linkValueProperties: Set[SmartIri] = Set.empty[SmartIri], + fileValueProperties: Set[SmartIri] = Set.empty[SmartIri] +) extends ReadEntityInfoV2 with KnoraReadV2[ReadClassInfoV2] { /** - * All the class's cardinalities, both direct and indirect. - */ - lazy val allCardinalities - : Map[SmartIri, KnoraCardinalityInfo] = inheritedCardinalities ++ entityInfoContent.directCardinalities + * All the class's cardinalities, both direct and indirect. + */ + lazy val allCardinalities: Map[SmartIri, KnoraCardinalityInfo] = + inheritedCardinalities ++ entityInfoContent.directCardinalities /** - * All the class's cardinalities for subproperties of `knora-base:resourceProperty`. - */ + * All the class's cardinalities for subproperties of `knora-base:resourceProperty`. + */ lazy val allResourcePropertyCardinalities: Map[SmartIri, KnoraCardinalityInfo] = allCardinalities.filter { case (propertyIri, _) => knoraResourceProperties.contains(propertyIri) } @@ -2547,16 +2760,16 @@ case class ReadClassInfoV2(entityInfoContent: ClassInfoContentV2, } val inheritedCardinalitiesConsideringLinkValueProps = if (targetSchema == ApiV2Simple) { - inheritedCardinalities.filterNot { - case (propertyIri, _) => linkValueProperties.contains(propertyIri) + inheritedCardinalities.filterNot { case (propertyIri, _) => + linkValueProperties.contains(propertyIri) } } else { inheritedCardinalities } val directCardinalitiesConsideringLinkValueProps = if (targetSchema == ApiV2Simple) { - entityInfoContent.directCardinalities.filterNot { - case (propertyIri, _) => linkValueProperties.contains(propertyIri) + entityInfoContent.directCardinalities.filterNot { case (propertyIri, _) => + linkValueProperties.contains(propertyIri) } } else { entityInfoContent.directCardinalities @@ -2571,8 +2784,8 @@ case class ReadClassInfoV2(entityInfoContent: ClassInfoContentV2, // Remove inherited cardinalities for internal properties that don't exist in the target schema. val inheritedCardinalitiesFilteredForTargetSchema: Map[SmartIri, KnoraCardinalityInfo] = - inheritedCardinalitiesConsideringLinkValueProps.filterNot { - case (propertyIri, _) => transformationRules.internalPropertiesToRemove.contains(propertyIri) + inheritedCardinalitiesConsideringLinkValueProps.filterNot { case (propertyIri, _) => + transformationRules.internalPropertiesToRemove.contains(propertyIri) } // Remove base classes that don't exist in the target schema. @@ -2590,13 +2803,13 @@ case class ReadClassInfoV2(entityInfoContent: ClassInfoContentV2, .toOntologySchema(targetSchema) val inheritedCardinalitiesInTargetSchema: Map[SmartIri, KnoraCardinalityInfo] = - inheritedCardinalitiesFilteredForTargetSchema.map { - case (propertyIri, cardinality) => propertyIri.toOntologySchema(targetSchema) -> cardinality + inheritedCardinalitiesFilteredForTargetSchema.map { case (propertyIri, cardinality) => + propertyIri.toOntologySchema(targetSchema) -> cardinality } val knoraResourcePropertiesInTargetSchema = knoraResourcePropertiesConsideringLinkValueProps.map(_.toOntologySchema(targetSchema)) - val linkPropertiesInTargetSchema = linkProperties.map(_.toOntologySchema(targetSchema)) + val linkPropertiesInTargetSchema = linkProperties.map(_.toOntologySchema(targetSchema)) val linkValuePropertiesInTargetSchema = linkValuePropsForSchema.map(_.toOntologySchema(targetSchema)) val fileValuePropertiesInTargetSchema = fileValueProperties.map(_.toOntologySchema(targetSchema)) @@ -2606,12 +2819,14 @@ case class ReadClassInfoV2(entityInfoContent: ClassInfoContentV2, val inheritedCardinalitiesToAdd: Map[SmartIri, KnoraCardinalityInfo] = baseClassesInTargetSchema.flatMap { baseClassIri => - transformationRules.externalCardinalitiesToAdd.getOrElse(baseClassIri, - Map.empty[SmartIri, KnoraCardinalityInfo]) + transformationRules.externalCardinalitiesToAdd.getOrElse( + baseClassIri, + Map.empty[SmartIri, KnoraCardinalityInfo] + ) }.toMap - val inheritedCardinalitiesWithExtraOnesForSchema - : Map[SmartIri, KnoraCardinalityInfo] = inheritedCardinalitiesInTargetSchema ++ inheritedCardinalitiesToAdd + val inheritedCardinalitiesWithExtraOnesForSchema: Map[SmartIri, KnoraCardinalityInfo] = + inheritedCardinalitiesInTargetSchema ++ inheritedCardinalitiesToAdd copy( entityInfoContent = entityInfoContentInTargetSchema, @@ -2627,51 +2842,49 @@ case class ReadClassInfoV2(entityInfoContent: ClassInfoContentV2, def getNonLanguageSpecific(targetSchema: ApiV2Schema): Map[IRI, JsonLDValue] = { if (entityInfoContent.ontologySchema != targetSchema) { throw DataConversionException( - s"ReadClassInfoV2 for class ${entityInfoContent.classIri} is not in schema $targetSchema") + s"ReadClassInfoV2 for class ${entityInfoContent.classIri} is not in schema $targetSchema" + ) } // Convert OWL cardinalities to JSON-LD. - val owlCardinalities: Seq[JsonLDObject] = allCardinalities.toArray - .sortBy { - case (propertyIri, _) => propertyIri - } - .sortBy { - case (_, cardinalityInfo: KnoraCardinalityInfo) => cardinalityInfo.guiOrder + val owlCardinalities: Seq[JsonLDObject] = allCardinalities.toArray.sortBy { case (propertyIri, _) => + propertyIri + }.sortBy { case (_, cardinalityInfo: KnoraCardinalityInfo) => + cardinalityInfo.guiOrder + }.map { case (propertyIri: SmartIri, cardinalityInfo: KnoraCardinalityInfo) => + val prop2card: (IRI, JsonLDInt) = cardinalityInfo.cardinality match { + case Cardinality.MayHaveMany => OntologyConstants.Owl.MinCardinality -> JsonLDInt(0) + case Cardinality.MayHaveOne => OntologyConstants.Owl.MaxCardinality -> JsonLDInt(1) + case Cardinality.MustHaveOne => OntologyConstants.Owl.Cardinality -> JsonLDInt(1) + case Cardinality.MustHaveSome => OntologyConstants.Owl.MinCardinality -> JsonLDInt(1) } - .map { - case (propertyIri: SmartIri, cardinalityInfo: KnoraCardinalityInfo) => - val prop2card: (IRI, JsonLDInt) = cardinalityInfo.cardinality match { - case Cardinality.MayHaveMany => OntologyConstants.Owl.MinCardinality -> JsonLDInt(0) - case Cardinality.MayHaveOne => OntologyConstants.Owl.MaxCardinality -> JsonLDInt(1) - case Cardinality.MustHaveOne => OntologyConstants.Owl.Cardinality -> JsonLDInt(1) - case Cardinality.MustHaveSome => OntologyConstants.Owl.MinCardinality -> JsonLDInt(1) - } - // If we're using the complex schema and the cardinality is inherited, add an annotation to say so. - val isInheritedStatement = - if (targetSchema == ApiV2Complex && !entityInfoContent.directCardinalities.contains(propertyIri)) { - Some(OntologyConstants.KnoraApiV2Complex.IsInherited -> JsonLDBoolean(true)) - } else { - None - } - - val guiOrderStatement = targetSchema match { - case ApiV2Complex => - cardinalityInfo.guiOrder.map { guiOrder => - OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiOrder -> JsonLDInt(guiOrder) - } + // If we're using the complex schema and the cardinality is inherited, add an annotation to say so. + val isInheritedStatement = + if (targetSchema == ApiV2Complex && !entityInfoContent.directCardinalities.contains(propertyIri)) { + Some(OntologyConstants.KnoraApiV2Complex.IsInherited -> JsonLDBoolean(true)) + } else { + None + } - case _ => None + val guiOrderStatement = targetSchema match { + case ApiV2Complex => + cardinalityInfo.guiOrder.map { guiOrder => + OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiOrder -> JsonLDInt(guiOrder) } - JsonLDObject( - Map( - JsonLDKeywords.TYPE -> JsonLDString(OntologyConstants.Owl.Restriction), - OntologyConstants.Owl.OnProperty -> JsonLDUtil.iriToJsonLDObject(propertyIri.toString), - prop2card - ) ++ isInheritedStatement ++ guiOrderStatement) + case _ => None } + JsonLDObject( + Map( + JsonLDKeywords.TYPE -> JsonLDString(OntologyConstants.Owl.Restriction), + OntologyConstants.Owl.OnProperty -> JsonLDUtil.iriToJsonLDObject(propertyIri.toString), + prop2card + ) ++ isInheritedStatement ++ guiOrderStatement + ) + } + val resourceIconPred = targetSchema match { case ApiV2Simple => OntologyConstants.KnoraApiV2Simple.ResourceIcon case ApiV2Complex => OntologyConstants.KnoraApiV2Complex.ResourceIcon @@ -2735,7 +2948,7 @@ case class ReadClassInfoV2(entityInfoContent: ClassInfoContentV2, } Map( - JsonLDKeywords.ID -> JsonLDString(entityInfoContent.classIri.toString), + JsonLDKeywords.ID -> JsonLDString(entityInfoContent.classIri.toString), JsonLDKeywords.TYPE -> JsonLDArray(entityInfoContent.getRdfTypes.map(typeIri => JsonLDString(typeIri.toString))) ) ++ jsonSubClassOfStatement ++ resourceIconStatement ++ isKnoraResourceClassStatement ++ isStandoffClassStatement ++ canBeInstantiatedStatement ++ isValueClassStatement ++ jsonRestriction @@ -2743,25 +2956,26 @@ case class ReadClassInfoV2(entityInfoContent: ClassInfoContentV2, } /** - * Represents an OWL property definition as returned in an API response. - * - * @param entityInfoContent a [[PropertyInfoContentV2]] providing information about the property. - * @param isResourceProp `true` if the property is a subproperty of `knora-base:resourceProperty`. - * @param isEditable `true` if the property's value is editable via the Knora API. - * @param isLinkProp `true` if the property is a subproperty of `knora-base:hasLinkTo`. - * @param isLinkValueProp `true` if the property is a subproperty of `knora-base:hasLinkToValue`. - * @param isFileValueProp `true` if the property is a subproperty of `knora-base:hasFileValue`. - * @param isStandoffInternalReferenceProperty if `true`, this is a subproperty (directly or indirectly) of - * [[OntologyConstants.KnoraBase.StandoffTagHasInternalReference]]. - */ -case class ReadPropertyInfoV2(entityInfoContent: PropertyInfoContentV2, - isResourceProp: Boolean = false, - isEditable: Boolean = false, - isLinkProp: Boolean = false, - isLinkValueProp: Boolean = false, - isFileValueProp: Boolean = false, - isStandoffInternalReferenceProperty: Boolean = false) - extends ReadEntityInfoV2 + * Represents an OWL property definition as returned in an API response. + * + * @param entityInfoContent a [[PropertyInfoContentV2]] providing information about the property. + * @param isResourceProp `true` if the property is a subproperty of `knora-base:resourceProperty`. + * @param isEditable `true` if the property's value is editable via the Knora API. + * @param isLinkProp `true` if the property is a subproperty of `knora-base:hasLinkTo`. + * @param isLinkValueProp `true` if the property is a subproperty of `knora-base:hasLinkToValue`. + * @param isFileValueProp `true` if the property is a subproperty of `knora-base:hasFileValue`. + * @param isStandoffInternalReferenceProperty if `true`, this is a subproperty (directly or indirectly) of + * [[OntologyConstants.KnoraBase.StandoffTagHasInternalReference]]. + */ +case class ReadPropertyInfoV2( + entityInfoContent: PropertyInfoContentV2, + isResourceProp: Boolean = false, + isEditable: Boolean = false, + isLinkProp: Boolean = false, + isLinkValueProp: Boolean = false, + isFileValueProp: Boolean = false, + isStandoffInternalReferenceProperty: Boolean = false +) extends ReadEntityInfoV2 with KnoraReadV2[ReadPropertyInfoV2] { override def toOntologySchema(targetSchema: ApiV2Schema): ReadPropertyInfoV2 = copy( entityInfoContent = entityInfoContent.toOntologySchema(targetSchema) @@ -2770,7 +2984,8 @@ case class ReadPropertyInfoV2(entityInfoContent: PropertyInfoContentV2, def getNonLanguageSpecific(targetSchema: ApiV2Schema): Map[IRI, JsonLDValue] = { if (entityInfoContent.ontologySchema != targetSchema) { throw DataConversionException( - s"ReadPropertyInfoV2 for property ${entityInfoContent.propertyIri} is not in schema $targetSchema") + s"ReadPropertyInfoV2 for property ${entityInfoContent.propertyIri} is not in schema $targetSchema" + ) } // Get the correct knora-api:subjectType and knora-api:objectType predicates for the target API schema. @@ -2786,15 +3001,20 @@ case class ReadPropertyInfoV2(entityInfoContent: PropertyInfoContentV2, entityInfoContent.ontologySchema match { case InternalSchema => throw DataConversionException( - s"ReadPropertyInfoV2 for property ${entityInfoContent.propertyIri} is not in schema $targetSchema") + s"ReadPropertyInfoV2 for property ${entityInfoContent.propertyIri} is not in schema $targetSchema" + ) case ApiV2Simple => - (entityInfoContent.getPredicateIriObject(OntologyConstants.KnoraApiV2Simple.SubjectType.toSmartIri), - entityInfoContent.getPredicateIriObject(OntologyConstants.KnoraApiV2Simple.ObjectType.toSmartIri)) + ( + entityInfoContent.getPredicateIriObject(OntologyConstants.KnoraApiV2Simple.SubjectType.toSmartIri), + entityInfoContent.getPredicateIriObject(OntologyConstants.KnoraApiV2Simple.ObjectType.toSmartIri) + ) case ApiV2Complex => - (entityInfoContent.getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri), - entityInfoContent.getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri)) + ( + entityInfoContent.getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri), + entityInfoContent.getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri) + ) } // Make the property's knora-api:subjectType and knora-api:objectType statements. @@ -2850,11 +3070,14 @@ case class ReadPropertyInfoV2(entityInfoContent: PropertyInfoContentV2, val guiAttributeStatement = if (targetSchema == ApiV2Complex) { entityInfoContent.getPredicateStringLiteralObjectsWithoutLang( - OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri) match { + OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri + ) match { case objs if objs.nonEmpty => Some( OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute -> JsonLDArray( - objs.toArray.sorted.map(JsonLDString))) + objs.toArray.sorted.map(JsonLDString) + ) + ) case _ => None } @@ -2863,7 +3086,7 @@ case class ReadPropertyInfoV2(entityInfoContent: PropertyInfoContentV2, } Map( - JsonLDKeywords.ID -> JsonLDString(entityInfoContent.propertyIri.toString), + JsonLDKeywords.ID -> JsonLDString(entityInfoContent.propertyIri.toString), JsonLDKeywords.TYPE -> JsonLDArray(entityInfoContent.getRdfTypes.map(typeIri => JsonLDString(typeIri.toString))) ) ++ jsonSubPropertyOfStatement ++ subjectTypeStatement ++ objectTypeStatement ++ isResourcePropStatement ++ isEditableStatement ++ isLinkValuePropertyStatement ++ @@ -2872,10 +3095,10 @@ case class ReadPropertyInfoV2(entityInfoContent: PropertyInfoContentV2, } /** - * Represents an OWL named individual definition as returned in an API response. - * - * @param entityInfoContent an [[IndividualInfoContentV2]] representing information about the named individual. - */ + * Represents an OWL named individual definition as returned in an API response. + * + * @param entityInfoContent an [[IndividualInfoContentV2]] representing information about the named individual. + */ case class ReadIndividualInfoV2(entityInfoContent: IndividualInfoContentV2) extends ReadEntityInfoV2 with KnoraReadV2[ReadIndividualInfoV2] { @@ -2887,13 +3110,12 @@ case class ReadIndividualInfoV2(entityInfoContent: IndividualInfoContentV2) val jsonLDPredicates: Map[IRI, JsonLDValue] = entityInfoContent.predicates.foldLeft(Map.empty[IRI, JsonLDValue]) { case (acc, (predicateIri, predicateInfo)) => if (predicateInfo.objects.nonEmpty) { - val nonLanguageSpecificObjectsAsJson: Seq[JsonLDValue] = predicateInfo.objects - .collect { - case StringLiteralV2(str, None) => (JsonLDString(str), str) - case SmartIriLiteralV2(iri) => - val iriStr = iri.toString - (JsonLDUtil.iriToJsonLDObject(iri.toString), iriStr) - } + val nonLanguageSpecificObjectsAsJson: Seq[JsonLDValue] = predicateInfo.objects.collect { + case StringLiteralV2(str, None) => (JsonLDString(str), str) + case SmartIriLiteralV2(iri) => + val iriStr = iri.toString + (JsonLDUtil.iriToJsonLDObject(iri.toString), iriStr) + } .sortBy(_._2) .map(_._1) // Sort for determinism in testing. @@ -2910,33 +3132,33 @@ case class ReadIndividualInfoV2(entityInfoContent: IndividualInfoContentV2) } /** - * Represents a definition of an `rdfs:Datatype`. - * - * @param onDatatype the base datatype to be extended. - * @param pattern an optional `xsd:pattern` specifying the regular expression that restricts its values. - */ + * Represents a definition of an `rdfs:Datatype`. + * + * @param onDatatype the base datatype to be extended. + * @param pattern an optional `xsd:pattern` specifying the regular expression that restricts its values. + */ case class DatatypeInfoV2(onDatatype: SmartIri, pattern: Option[String] = None) /** - * Represents assertions about an OWL class. - * - * @param classIri the IRI of the class. - * @param predicates a [[Map]] of predicate IRIs to [[PredicateInfoV2]] objects. - * @param directCardinalities a [[Map]] of properties to [[Cardinality.Value]] objects representing the cardinalities - * that are directly defined on the class (as opposed to inherited) on those properties. - * @param datatypeInfo if the class's `rdf:type` is `rdfs:Datatype`, a [[DatatypeInfoV2]] describing it. - * @param subClassOf the classes that this class is a subclass of. - * @param ontologySchema indicates whether this ontology entity belongs to an internal ontology (for use in the - * triplestore) or an external one (for use in the Knora API). - */ -case class ClassInfoContentV2(classIri: SmartIri, - predicates: Map[SmartIri, PredicateInfoV2] = Map.empty[SmartIri, PredicateInfoV2], - directCardinalities: Map[SmartIri, KnoraCardinalityInfo] = - Map.empty[SmartIri, KnoraCardinalityInfo], - datatypeInfo: Option[DatatypeInfoV2] = None, - subClassOf: Set[SmartIri] = Set.empty[SmartIri], - ontologySchema: OntologySchema) - extends EntityInfoContentV2 + * Represents assertions about an OWL class. + * + * @param classIri the IRI of the class. + * @param predicates a [[Map]] of predicate IRIs to [[PredicateInfoV2]] objects. + * @param directCardinalities a [[Map]] of properties to [[Cardinality.Value]] objects representing the cardinalities + * that are directly defined on the class (as opposed to inherited) on those properties. + * @param datatypeInfo if the class's `rdf:type` is `rdfs:Datatype`, a [[DatatypeInfoV2]] describing it. + * @param subClassOf the classes that this class is a subclass of. + * @param ontologySchema indicates whether this ontology entity belongs to an internal ontology (for use in the + * triplestore) or an external one (for use in the Knora API). + */ +case class ClassInfoContentV2( + classIri: SmartIri, + predicates: Map[SmartIri, PredicateInfoV2] = Map.empty[SmartIri, PredicateInfoV2], + directCardinalities: Map[SmartIri, KnoraCardinalityInfo] = Map.empty[SmartIri, KnoraCardinalityInfo], + datatypeInfo: Option[DatatypeInfoV2] = None, + subClassOf: Set[SmartIri] = Set.empty[SmartIri], + ontologySchema: OntologySchema +) extends EntityInfoContentV2 with KnoraContentV2[ClassInfoContentV2] { override def toOntologySchema(targetSchema: OntologySchema): ClassInfoContentV2 = { val classIriInTargetSchema = classIri.toOntologySchema(targetSchema) @@ -2956,8 +3178,8 @@ case class ClassInfoContentV2(classIri: SmartIri, } val directCardinalitiesFilteredForTargetSchema: Map[SmartIri, KnoraCardinalityInfo] = - directCardinalities.filterNot { - case (propertyIri, _) => knoraBasePropertiesToRemove.contains(propertyIri) + directCardinalities.filterNot { case (propertyIri, _) => + knoraBasePropertiesToRemove.contains(propertyIri) } val subClassOfFilteredForTargetSchema = subClassOf.filterNot { baseClass => @@ -2970,8 +3192,8 @@ case class ClassInfoContentV2(classIri: SmartIri, // Convert the property IRIs of the remaining cardinalities to the target schema. val directCardinalitiesInTargetSchema: Map[SmartIri, KnoraCardinalityInfo] = - directCardinalitiesFilteredForTargetSchema.map { - case (propertyIri, cardinality) => propertyIri.toOntologySchema(targetSchema) -> cardinality + directCardinalitiesFilteredForTargetSchema.map { case (propertyIri, cardinality) => + propertyIri.toOntologySchema(targetSchema) -> cardinality } // Add any cardinalities that this class has in the external schema but not in the internal schema. @@ -2986,12 +3208,11 @@ case class ClassInfoContentV2(classIri: SmartIri, case None => Map.empty[SmartIri, KnoraCardinalityInfo] } - val directCardinalitiesWithExtraOnesForSchema - : Map[SmartIri, KnoraCardinalityInfo] = directCardinalitiesInTargetSchema ++ cardinalitiesToAdd + val directCardinalitiesWithExtraOnesForSchema: Map[SmartIri, KnoraCardinalityInfo] = + directCardinalitiesInTargetSchema ++ cardinalitiesToAdd - val predicatesInTargetSchema = predicates.map { - case (predicateIri, predicate) => - predicateIri.toOntologySchema(targetSchema) -> predicate.toOntologySchema(targetSchema) + val predicatesInTargetSchema = predicates.map { case (predicateIri, predicate) => + predicateIri.toOntologySchema(targetSchema) -> predicate.toOntologySchema(targetSchema) } val subClassOfInTargetSchema = subClassOfFilteredForTargetSchema.map(_.toOntologySchema(targetSchema)) @@ -3020,23 +3241,23 @@ case class ClassInfoContentV2(classIri: SmartIri, override def getRdfTypes: Seq[SmartIri] = requireIriObjects( OntologyConstants.Rdf.Type.toSmartIri, - throw InconsistentRepositoryDataException(s"The rdf:type of $classIri is missing or invalid")).toVector.sorted + throw InconsistentRepositoryDataException(s"The rdf:type of $classIri is missing or invalid") + ).toVector.sorted /** - * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used after an update, when the - * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data - * read back from the triplestore (in which predicate objects are not escaped). - * - * @return a copy of this object with its predicate objects unescaped. - */ - def unescape: ClassInfoContentV2 = { + * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used after an update, when the + * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data + * read back from the triplestore (in which predicate objects are not escaped). + * + * @return a copy of this object with its predicate objects unescaped. + */ + def unescape: ClassInfoContentV2 = copy(predicates = unescapePredicateObjects) - } } /** - * Can read a [[ClassInfoContentV2]] from JSON-LD. - */ + * Can read a [[ClassInfoContentV2]] from JSON-LD. + */ object ClassInfoContentV2 { // The predicates that are allowed in a class definition that is read from JSON-LD representing client input. @@ -3059,12 +3280,12 @@ object ClassInfoContentV2 { ) /** - * Converts a JSON-LD class definition into a [[ClassInfoContentV2]]. - * - * @param jsonLDClassDef a JSON-LD object representing a class definition. - * @param parsingMode the parsing mode to be used. - * @return a [[ClassInfoContentV2]] representing the class definition. - */ + * Converts a JSON-LD class definition into a [[ClassInfoContentV2]]. + * + * @param jsonLDClassDef a JSON-LD object representing a class definition. + * @param parsingMode the parsing mode to be used. + * @return a [[ClassInfoContentV2]] representing the class definition. + */ def fromJsonLDObject(jsonLDClassDef: JsonLDObject, parsingMode: InputOntologyParsingModeV2): ClassInfoContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -3081,7 +3302,8 @@ object ClassInfoContentV2 { if (extraClassPredicates.nonEmpty) { throw BadRequestException( - s"The definition of $classIri contains one or more invalid predicates: ${extraClassPredicates.mkString(", ")}") + s"The definition of $classIri contains one or more invalid predicates: ${extraClassPredicates.mkString(", ")}" + ) } else { jsonLDClassDef } @@ -3104,14 +3326,11 @@ object ClassInfoContentV2 { } // Get the base classes from the objects of rdfs:subClassOf. - val baseClasses: Set[SmartIri] = arrayElemsAsObjs - .filter { jsonLDObj => - jsonLDObj.isIri - } - .map { jsonLDObj => - jsonLDObj.toIri(stringFormatter.toSmartIriWithErr) - } - .toSet + val baseClasses: Set[SmartIri] = arrayElemsAsObjs.filter { jsonLDObj => + jsonLDObj.isIri + }.map { jsonLDObj => + jsonLDObj.toIri(stringFormatter.toSmartIriWithErr) + }.toSet // The restrictions are the object of rdfs:subClassOf that have type owl:Restriction. val restrictions: Seq[JsonLDObject] = arrayElemsAsObjs.filter { jsonLDObj => @@ -3125,65 +3344,67 @@ object ClassInfoContentV2 { } val cardinalities: Map[SmartIri, KnoraCardinalityInfo] = - restrictions.foldLeft(Map.empty[SmartIri, KnoraCardinalityInfo]) { - case (acc, restriction) => - val isInherited = - restriction.value.get(OntologyConstants.KnoraApiV2Complex.IsInherited).contains(JsonLDBoolean(true)) - - // If we're in client input mode and the client tries to submit an inherited cardinality, return - // a helpful error message. - if (isInherited && parsingMode == ClientInputParsingModeV2) { - throw BadRequestException("Inherited cardinalities are not allowed in this request") - } else if (isInherited && parsingMode == TestResponseParsingModeV2) { - // In test response parsing mode, ignore inherited cardinalities. - acc - } else { - // In client input mode, only certain predicates are allowed on owl:Restriction nodes. - if (parsingMode == ClientInputParsingModeV2) { - val extraRestrictionPredicates = restriction.value.keySet -- AllowedJsonLDRestrictionPredicatesInClientInput - - if (extraRestrictionPredicates.nonEmpty) { - throw BadRequestException( - s"A cardinality in the definition of $classIri contains one or more invalid predicates: ${extraRestrictionPredicates - .mkString(", ")}") - } + restrictions.foldLeft(Map.empty[SmartIri, KnoraCardinalityInfo]) { case (acc, restriction) => + val isInherited = + restriction.value.get(OntologyConstants.KnoraApiV2Complex.IsInherited).contains(JsonLDBoolean(true)) + + // If we're in client input mode and the client tries to submit an inherited cardinality, return + // a helpful error message. + if (isInherited && parsingMode == ClientInputParsingModeV2) { + throw BadRequestException("Inherited cardinalities are not allowed in this request") + } else if (isInherited && parsingMode == TestResponseParsingModeV2) { + // In test response parsing mode, ignore inherited cardinalities. + acc + } else { + // In client input mode, only certain predicates are allowed on owl:Restriction nodes. + if (parsingMode == ClientInputParsingModeV2) { + val extraRestrictionPredicates = + restriction.value.keySet -- AllowedJsonLDRestrictionPredicatesInClientInput + + if (extraRestrictionPredicates.nonEmpty) { + throw BadRequestException( + s"A cardinality in the definition of $classIri contains one or more invalid predicates: ${extraRestrictionPredicates + .mkString(", ")}" + ) } - - val (owlCardinalityIri: IRI, owlCardinalityValue: Int) = - restriction.maybeInt(OntologyConstants.Owl.Cardinality) match { - case Some(value) => OntologyConstants.Owl.Cardinality -> value - - case None => - restriction.maybeInt(OntologyConstants.Owl.MinCardinality) match { - case Some(value) => OntologyConstants.Owl.MinCardinality -> value - - case None => - restriction.maybeInt(OntologyConstants.Owl.MaxCardinality) match { - case Some(value) => OntologyConstants.Owl.MaxCardinality -> value - case None => - throw BadRequestException( - s"Missing OWL cardinality predicate in the definition of $classIri") - } - } - } - - val onProperty = - restriction.requireIriInObject(OntologyConstants.Owl.OnProperty, stringFormatter.toSmartIriWithErr) - val guiOrder = restriction.maybeInt(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiOrder) - - val owlCardinalityInfo = OwlCardinalityInfo( - owlCardinalityIri = owlCardinalityIri, - owlCardinalityValue = owlCardinalityValue, - guiOrder = guiOrder - ) - - val knoraCardinalityInfo = Cardinality.owlCardinality2KnoraCardinality( - propertyIri = onProperty.toString, - owlCardinality = owlCardinalityInfo - ) - - acc + (onProperty -> knoraCardinalityInfo) } + + val (owlCardinalityIri: IRI, owlCardinalityValue: Int) = + restriction.maybeInt(OntologyConstants.Owl.Cardinality) match { + case Some(value) => OntologyConstants.Owl.Cardinality -> value + + case None => + restriction.maybeInt(OntologyConstants.Owl.MinCardinality) match { + case Some(value) => OntologyConstants.Owl.MinCardinality -> value + + case None => + restriction.maybeInt(OntologyConstants.Owl.MaxCardinality) match { + case Some(value) => OntologyConstants.Owl.MaxCardinality -> value + case None => + throw BadRequestException( + s"Missing OWL cardinality predicate in the definition of $classIri" + ) + } + } + } + + val onProperty = + restriction.requireIriInObject(OntologyConstants.Owl.OnProperty, stringFormatter.toSmartIriWithErr) + val guiOrder = restriction.maybeInt(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiOrder) + + val owlCardinalityInfo = OwlCardinalityInfo( + owlCardinalityIri = owlCardinalityIri, + owlCardinalityValue = owlCardinalityValue, + guiOrder = guiOrder + ) + + val knoraCardinalityInfo = Cardinality.owlCardinality2KnoraCardinality( + propertyIri = onProperty.toString, + owlCardinality = owlCardinalityInfo + ) + + acc + (onProperty -> knoraCardinalityInfo) + } } (baseClasses, cardinalities) @@ -3231,19 +3452,20 @@ object ClassInfoContentV2 { } /** - * Represents assertions about an RDF property. - * - * @param propertyIri the IRI of the queried property. - * @param predicates a [[Map]] of predicate IRIs to [[PredicateInfoV2]] objects. - * @param subPropertyOf the property's direct superproperties. - * @param ontologySchema indicates whether this ontology entity belongs to an internal ontology (for use in the - * triplestore) or an external one (for use in the Knora API). - */ -case class PropertyInfoContentV2(propertyIri: SmartIri, - predicates: Map[SmartIri, PredicateInfoV2] = Map.empty[SmartIri, PredicateInfoV2], - subPropertyOf: Set[SmartIri] = Set.empty[SmartIri], - ontologySchema: OntologySchema) - extends EntityInfoContentV2 + * Represents assertions about an RDF property. + * + * @param propertyIri the IRI of the queried property. + * @param predicates a [[Map]] of predicate IRIs to [[PredicateInfoV2]] objects. + * @param subPropertyOf the property's direct superproperties. + * @param ontologySchema indicates whether this ontology entity belongs to an internal ontology (for use in the + * triplestore) or an external one (for use in the Knora API). + */ +case class PropertyInfoContentV2( + propertyIri: SmartIri, + predicates: Map[SmartIri, PredicateInfoV2] = Map.empty[SmartIri, PredicateInfoV2], + subPropertyOf: Set[SmartIri] = Set.empty[SmartIri], + ontologySchema: OntologySchema +) extends EntityInfoContentV2 with KnoraContentV2[PropertyInfoContentV2] { override def toOntologySchema(targetSchema: OntologySchema): PropertyInfoContentV2 = { @@ -3254,19 +3476,22 @@ case class PropertyInfoContentV2(propertyIri: SmartIri, // Yes. Is this an object property? val rdfTypeIri = OntologyConstants.Rdf.Type.toSmartIri val sourcePropertyType: SmartIri = getPredicateIriObject(rdfTypeIri).getOrElse( - throw InconsistentRepositoryDataException(s"Property $propertyIri has no rdf:type")) + throw InconsistentRepositoryDataException(s"Property $propertyIri has no rdf:type") + ) if (sourcePropertyType.toString == OntologyConstants.Owl.ObjectProperty) { // Yes. See if we need to change it to a datatype property. Does it have a knora-base:objectClassConstraint? - val objectClassConstraintIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri + val objectClassConstraintIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri val maybeObjectType: Option[SmartIri] = getPredicateIriObject(objectClassConstraintIri) maybeObjectType match { case Some(objectTypeObj) => // Yes. Is there a corresponding type in the API v2 simple ontology? - if (OntologyConstants - .CorrespondingIris((InternalSchema, ApiV2Simple)) - .contains(objectTypeObj.toString)) { + if ( + OntologyConstants + .CorrespondingIris((InternalSchema, ApiV2Simple)) + .contains(objectTypeObj.toString) + ) { // Yes. The corresponding type must be a datatype, so make this a datatype property. (predicates - rdfTypeIri) + (rdfTypeIri -> PredicateInfoV2( @@ -3302,9 +3527,8 @@ case class PropertyInfoContentV2(propertyIri: SmartIri, // Convert all IRIs to the target schema. - val predicatesInTargetSchema = predicatesWithAdjustedRdfType.map { - case (predicateIri, predicate) => - predicateIri.toOntologySchema(targetSchema) -> predicate.toOntologySchema(targetSchema) + val predicatesInTargetSchema = predicatesWithAdjustedRdfType.map { case (predicateIri, predicate) => + predicateIri.toOntologySchema(targetSchema) -> predicate.toOntologySchema(targetSchema) } val subPropertyOfInTargetSchema = subPropertyOfFilteredForTargetSchema.map(_.toOntologySchema(targetSchema)) @@ -3332,23 +3556,23 @@ case class PropertyInfoContentV2(propertyIri: SmartIri, override def getRdfTypes: Seq[SmartIri] = requireIriObjects( OntologyConstants.Rdf.Type.toSmartIri, - throw InconsistentRepositoryDataException(s"The rdf:type of $propertyIri is missing or invalid")).toVector.sorted + throw InconsistentRepositoryDataException(s"The rdf:type of $propertyIri is missing or invalid") + ).toVector.sorted /** - * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used after an update, when the - * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data - * read back from the triplestore (in which predicate objects are not escaped). - * - * @return a copy of this object with its predicate objects unescaped. - */ - def unescape: PropertyInfoContentV2 = { + * Undoes the SPARQL-escaping of predicate objects. This method is meant to be used after an update, when the + * input (whose predicate objects have been escaped for use in SPARQL) needs to be compared with the updated data + * read back from the triplestore (in which predicate objects are not escaped). + * + * @return a copy of this object with its predicate objects unescaped. + */ + def unescape: PropertyInfoContentV2 = copy(predicates = unescapePredicateObjects) - } } /** - * Can read a [[PropertyInfoContentV2]] from JSON-LD, and provides constants used by that class. - */ + * Can read a [[PropertyInfoContentV2]] from JSON-LD, and provides constants used by that class. + */ object PropertyInfoContentV2 { // The predicates allowed in a property definition that is read from JSON-LD representing client input. private val AllowedJsonLDPropertyPredicatesInClientInput = Set( @@ -3366,14 +3590,16 @@ object PropertyInfoContentV2 { ) /** - * Reads a [[PropertyInfoContentV2]] from a JSON-LD object. - * - * @param jsonLDPropertyDef the JSON-LD object representing a property definition. - * @param parsingMode the parsing mode to be used. - * @return a [[PropertyInfoContentV2]] representing the property definition. - */ - def fromJsonLDObject(jsonLDPropertyDef: JsonLDObject, - parsingMode: InputOntologyParsingModeV2): PropertyInfoContentV2 = { + * Reads a [[PropertyInfoContentV2]] from a JSON-LD object. + * + * @param jsonLDPropertyDef the JSON-LD object representing a property definition. + * @param parsingMode the parsing mode to be used. + * @return a [[PropertyInfoContentV2]] representing the property definition. + */ + def fromJsonLDObject( + jsonLDPropertyDef: JsonLDObject, + parsingMode: InputOntologyParsingModeV2 + ): PropertyInfoContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val propertyIri: SmartIri = @@ -3389,7 +3615,8 @@ object PropertyInfoContentV2 { if (extraPropertyPredicates.nonEmpty) { throw BadRequestException( - s"The definition of $propertyIri contains one or more invalid predicates: ${extraPropertyPredicates.mkString(", ")}") + s"The definition of $propertyIri contains one or more invalid predicates: ${extraPropertyPredicates.mkString(", ")}" + ) } else { jsonLDPropertyDef } @@ -3423,58 +3650,59 @@ object PropertyInfoContentV2 { } /** - * Represents assertions about an OWL named individual. - * - * @param individualIri the IRI of the named individual. - * @param predicates the predicates of the named individual. - * @param ontologySchema indicates whether this named individual belongs to an internal ontology (for use in the - * triplestore) or an external one (for use in the Knora API). - */ -case class IndividualInfoContentV2(individualIri: SmartIri, - predicates: Map[SmartIri, PredicateInfoV2], - ontologySchema: OntologySchema) - extends EntityInfoContentV2 + * Represents assertions about an OWL named individual. + * + * @param individualIri the IRI of the named individual. + * @param predicates the predicates of the named individual. + * @param ontologySchema indicates whether this named individual belongs to an internal ontology (for use in the + * triplestore) or an external one (for use in the Knora API). + */ +case class IndividualInfoContentV2( + individualIri: SmartIri, + predicates: Map[SmartIri, PredicateInfoV2], + ontologySchema: OntologySchema +) extends EntityInfoContentV2 with KnoraContentV2[IndividualInfoContentV2] { override def getRdfType: SmartIri = { val rdfTypePred = predicates.getOrElse( OntologyConstants.Rdf.Type.toSmartIri, - throw InconsistentRepositoryDataException(s"OWL named individual $individualIri has no rdf:type")) + throw InconsistentRepositoryDataException(s"OWL named individual $individualIri has no rdf:type") + ) val nonIndividualTypes: Seq[SmartIri] = getRdfTypes.filter(iri => iri.toString != OntologyConstants.Owl.NamedIndividual) if (nonIndividualTypes.size != 1) { throw InconsistentRepositoryDataException( - s"OWL named individual $individualIri has too many objects for rdf:type: ${rdfTypePred.objects.mkString(", ")}") + s"OWL named individual $individualIri has too many objects for rdf:type: ${rdfTypePred.objects.mkString(", ")}" + ) } nonIndividualTypes.head } override def getRdfTypes: Seq[SmartIri] = - requireIriObjects(OntologyConstants.Rdf.Type.toSmartIri, - throw InconsistentRepositoryDataException( - s"The rdf:type of $individualIri is missing or invalid")).toVector.sorted + requireIriObjects( + OntologyConstants.Rdf.Type.toSmartIri, + throw InconsistentRepositoryDataException(s"The rdf:type of $individualIri is missing or invalid") + ).toVector.sorted - override def toOntologySchema(targetSchema: OntologySchema): IndividualInfoContentV2 = { + override def toOntologySchema(targetSchema: OntologySchema): IndividualInfoContentV2 = copy( individualIri = individualIri.toOntologySchema(targetSchema), - predicates = predicates.map { - case (predicateIri, predicate) => - predicateIri.toOntologySchema(targetSchema) -> predicate.toOntologySchema(targetSchema) + predicates = predicates.map { case (predicateIri, predicate) => + predicateIri.toOntologySchema(targetSchema) -> predicate.toOntologySchema(targetSchema) }, ontologySchema = targetSchema ) - } - def unescape: IndividualInfoContentV2 = { + def unescape: IndividualInfoContentV2 = copy(predicates = unescapePredicateObjects) - } } /** - * Can read an [[IndividualInfoContentV2]] from JSON-LD. - */ + * Can read an [[IndividualInfoContentV2]] from JSON-LD. + */ object IndividualInfoContentV2 { def fromJsonLDObject(jsonLDIndividualDef: JsonLDObject): IndividualInfoContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -3482,7 +3710,8 @@ object IndividualInfoContentV2 { val individualIri: SmartIri = jsonLDIndividualDef.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.toSmartIriWithErr) val ontologySchema: OntologySchema = individualIri.getOntologySchema.getOrElse( - throw BadRequestException(s"Invalid named individual IRI: $individualIri")) + throw BadRequestException(s"Invalid named individual IRI: $individualIri") + ) IndividualInfoContentV2( individualIri = individualIri, @@ -3494,48 +3723,51 @@ object IndividualInfoContentV2 { } /** - * Represents the IRIs of Knora entities (Knora resource classes, standoff class, resource properties, and standoff properties) defined in a particular ontology. - * - * @param ontologyIri the IRI of the ontology. - * @param classIris the classes defined in the ontology. - * @param propertyIris the properties defined in the ontology. - * @param standoffClassIris the standoff classes defined in the ontology. - * @param standoffPropertyIris the standoff properties defined in the ontology. - */ -case class OntologyKnoraEntitiesIriInfoV2(ontologyIri: SmartIri, - classIris: Set[SmartIri], - propertyIris: Set[SmartIri], - standoffClassIris: Set[SmartIri], - standoffPropertyIris: Set[SmartIri]) - -/** - * Represents information about a subclass of a resource class. - * - * @param id the IRI of the subclass. - * @param label the `rdfs:label` of the subclass. - */ + * Represents the IRIs of Knora entities (Knora resource classes, standoff class, resource properties, and standoff properties) defined in a particular ontology. + * + * @param ontologyIri the IRI of the ontology. + * @param classIris the classes defined in the ontology. + * @param propertyIris the properties defined in the ontology. + * @param standoffClassIris the standoff classes defined in the ontology. + * @param standoffPropertyIris the standoff properties defined in the ontology. + */ +case class OntologyKnoraEntitiesIriInfoV2( + ontologyIri: SmartIri, + classIris: Set[SmartIri], + propertyIris: Set[SmartIri], + standoffClassIris: Set[SmartIri], + standoffPropertyIris: Set[SmartIri] +) + +/** + * Represents information about a subclass of a resource class. + * + * @param id the IRI of the subclass. + * @param label the `rdfs:label` of the subclass. + */ case class SubClassInfoV2(id: SmartIri, label: String) /** - * Returns metadata about an ontology. - * - * @param ontologyIri the IRI of the ontology. - * @param projectIri the IRI of the project that the ontology belongs to. - * @param label the label of the ontology, if any. - * @param comment the comment of the ontology, if any. - * @param lastModificationDate the ontology's last modification date, if any. - * @param ontologyVersion the version string attached to the ontology, if any. - */ -case class OntologyMetadataV2(ontologyIri: SmartIri, - projectIri: Option[SmartIri] = None, - label: Option[String] = None, - comment: Option[String] = None, - lastModificationDate: Option[Instant] = None, - ontologyVersion: Option[String] = None) - extends KnoraContentV2[OntologyMetadataV2] { + * Returns metadata about an ontology. + * + * @param ontologyIri the IRI of the ontology. + * @param projectIri the IRI of the project that the ontology belongs to. + * @param label the label of the ontology, if any. + * @param comment the comment of the ontology, if any. + * @param lastModificationDate the ontology's last modification date, if any. + * @param ontologyVersion the version string attached to the ontology, if any. + */ +case class OntologyMetadataV2( + ontologyIri: SmartIri, + projectIri: Option[SmartIri] = None, + label: Option[String] = None, + comment: Option[String] = None, + lastModificationDate: Option[Instant] = None, + ontologyVersion: Option[String] = None +) extends KnoraContentV2[OntologyMetadataV2] { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - override def toOntologySchema(targetSchema: OntologySchema): OntologyMetadataV2 = { + override def toOntologySchema(targetSchema: OntologySchema): OntologyMetadataV2 = if (ontologyIri == OntologyConstants.KnoraBase.KnoraBaseOntologyIri.toSmartIri) { targetSchema match { case InternalSchema => this @@ -3548,20 +3780,21 @@ case class OntologyMetadataV2(ontologyIri: SmartIri, ) } - } /** - * Undoes the SPARQL-escaping of the `rdfs:label` and `rdfs:comment` of this ontology. This method is meant to be used in tests after an update, when the - * input (which has been escaped for use in SPARQL) needs to be compared with the updated data - * read back from the triplestore (which is not escaped). - * - * @return a copy of this [[OntologyMetadataV2]] with the `rdfs:label` and `rdfs:comment` unescaped. - */ + * Undoes the SPARQL-escaping of the `rdfs:label` and `rdfs:comment` of this ontology. This method is meant to be used in tests after an update, when the + * input (which has been escaped for use in SPARQL) needs to be compared with the updated data + * read back from the triplestore (which is not escaped). + * + * @return a copy of this [[OntologyMetadataV2]] with the `rdfs:label` and `rdfs:comment` unescaped. + */ def unescape: OntologyMetadataV2 = { val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - copy(label = label.map(stringFormatter.fromSparqlEncodedString), - comment = comment.map(stringFormatter.fromSparqlEncodedString)) + copy( + label = label.map(stringFormatter.fromSparqlEncodedString), + comment = comment.map(stringFormatter.fromSparqlEncodedString) + ) } def toJsonLD(targetSchema: ApiV2Schema): Map[String, JsonLDValue] = { @@ -3569,7 +3802,8 @@ case class OntologyMetadataV2(ontologyIri: SmartIri, val projectIriStatement: Option[(IRI, JsonLDObject)] = if (targetSchema == ApiV2Complex) { projectIri.map { definedProjectIri => OntologyConstants.KnoraApiV2Complex.AttachedToProject -> JsonLDUtil.iriToJsonLDObject( - definedProjectIri.toString) + definedProjectIri.toString + ) } } else { None @@ -3609,7 +3843,8 @@ case class OntologyMetadataV2(ontologyIri: SmartIri, } Map( - JsonLDKeywords.ID -> JsonLDString(ontologyIri.toString), - JsonLDKeywords.TYPE -> JsonLDString(OntologyConstants.Owl.Ontology)) ++ projectIriStatement ++ labelStatement ++ commentStatement ++ lastModDateStatement ++ isSharedStatement ++ isBuiltInStatement + JsonLDKeywords.ID -> JsonLDString(ontologyIri.toString), + JsonLDKeywords.TYPE -> JsonLDString(OntologyConstants.Owl.Ontology) + ) ++ projectIriStatement ++ labelStatement ++ commentStatement ++ lastModDateStatement ++ isSharedStatement ++ isBuiltInStatement } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/BUILD.bazel b/webapi/src/main/scala/org/knora/webapi/responders/BUILD.bazel index de680f45a3..14e8dcfa32 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/BUILD.bazel +++ b/webapi/src/main/scala/org/knora/webapi/responders/BUILD.bazel @@ -15,6 +15,7 @@ scala_library( "//webapi/src/main/scala/org/knora/webapi/feature", "//webapi/src/main/scala/org/knora/webapi/instrumentation", "//webapi/src/main/scala/org/knora/webapi/messages", + "//webapi/src/main/scala/org/knora/webapi/responders/v2/ontology", "//webapi/src/main/scala/org/knora/webapi/settings", "//webapi/src/main/scala/org/knora/webapi/store/cacheservice/settings", "//webapi/src/main/scala/org/knora/webapi/util", diff --git a/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala b/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala index 8e1b4638cb..746c285719 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala @@ -38,142 +38,144 @@ import scala.concurrent.{ExecutionContext, Future} import scala.language.postfixOps /** - * Responder helper methods. - */ + * Responder helper methods. + */ object Responder { /** - * An responder use this method to handle unexpected request messages in a consistent way. - * - * @param message the message that was received. - * @param log a [[Logger]]. - * @param who the responder receiving the message. - */ + * An responder use this method to handle unexpected request messages in a consistent way. + * + * @param message the message that was received. + * @param log a [[Logger]]. + * @param who the responder receiving the message. + */ def handleUnexpectedMessage(message: Any, log: Logger, who: String): Future[Nothing] = { val unexpectedMessageException = UnexpectedMessageException( - s"$who received an unexpected message $message of type ${message.getClass.getCanonicalName}") + s"$who received an unexpected message $message of type ${message.getClass.getCanonicalName}" + ) FastFuture.failed(unexpectedMessageException) } } /** - * An abstract class providing values that are commonly used in Knora responders. - */ + * An abstract class providing values that are commonly used in Knora responders. + */ abstract class Responder(responderData: ResponderData) extends LazyLogging { /** - * The actor system. - */ + * The actor system. + */ protected implicit val system: ActorSystem = responderData.system /** - * The execution context for futures created in Knora actors. - */ + * The execution context for futures created in Knora actors. + */ protected implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) /** - * The application settings. - */ + * The application settings. + */ protected val settings: KnoraSettingsImpl = responderData.knoraSettings /** - * The Cache Service settings. - */ + * The Cache Service settings. + */ protected val cacheServiceSettings: CacheServiceSettings = responderData.cacheServiceSettings /** - * The main application actor. - */ + * The main application actor. + */ protected val appActor: ActorRef = responderData.appActor /** - * The main application actor forwards messages to the responder manager. - */ + * The main application actor forwards messages to the responder manager. + */ protected val responderManager: ActorRef = responderData.appActor /** - * The main application actor forwards messages to the store manager. - */ + * The main application actor forwards messages to the store manager. + */ protected val storeManager: ActorRef = responderData.appActor /** - * A string formatter. - */ + * A string formatter. + */ protected implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * The application's default timeout for `ask` messages. - */ + * The application's default timeout for `ask` messages. + */ protected implicit val timeout: Timeout = settings.defaultTimeout /** - * Provides logging - */ - protected val log: Logger = logger + * Provides logging + */ + protected val log: Logger = logger protected val loggingAdapter: LoggingAdapter = akka.event.Logging(system, this.getClass) /** - * Checks whether an entity is used in the triplestore. - * - * @param entityIri the IRI of the entity. - * @param ignoreKnoraConstraints if `true`, ignores the use of the entity in Knora subject or object constraints. - * @param ignoreRdfSubjectAndObject if `true`, ignores the use of the entity in `rdf:subject` and `rdf:object`. - * - * @return `true` if the entity is used. - */ - protected def isEntityUsed(entityIri: SmartIri, - ignoreKnoraConstraints: Boolean = false, - ignoreRdfSubjectAndObject: Boolean = false): Future[Boolean] = { + * Checks whether an entity is used in the triplestore. + * + * @param entityIri the IRI of the entity. + * @param ignoreKnoraConstraints if `true`, ignores the use of the entity in Knora subject or object constraints. + * @param ignoreRdfSubjectAndObject if `true`, ignores the use of the entity in `rdf:subject` and `rdf:object`. + * + * @return `true` if the entity is used. + */ + protected def isEntityUsed( + entityIri: SmartIri, + ignoreKnoraConstraints: Boolean = false, + ignoreRdfSubjectAndObject: Boolean = false + ): Future[Boolean] = for { isEntityUsedSparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .isEntityUsed( - triplestore = settings.triplestoreType, - entityIri = entityIri, - ignoreKnoraConstraints = ignoreKnoraConstraints, - ignoreRdfSubjectAndObject = ignoreRdfSubjectAndObject - ) - .toString()) + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .isEntityUsed( + triplestore = settings.triplestoreType, + entityIri = entityIri, + ignoreKnoraConstraints = ignoreKnoraConstraints, + ignoreRdfSubjectAndObject = ignoreRdfSubjectAndObject + ) + .toString() + ) isEntityUsedResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(isEntityUsedSparql)) - .mapTo[SparqlSelectResult] + .mapTo[SparqlSelectResult] } yield isEntityUsedResponse.results.bindings.nonEmpty - } /** - * Throws an exception if an entity is used in the triplestore. - * - * @param entityIri the IRI of the entity. - * @param errorFun a function that throws an exception. It will be called if the entity is used. - * @param ignoreKnoraConstraints if `true`, ignores the use of the entity in Knora subject or object constraints. - * @param ignoreRdfSubjectAndObject if `true`, ignores the use of the entity in `rdf:subject` and `rdf:object`. - */ - protected def throwIfEntityIsUsed(entityIri: SmartIri, - errorFun: => Nothing, - ignoreKnoraConstraints: Boolean = false, - ignoreRdfSubjectAndObject: Boolean = false): Future[Unit] = { - + * Throws an exception if an entity is used in the triplestore. + * + * @param entityIri the IRI of the entity. + * @param errorFun a function that throws an exception. It will be called if the entity is used. + * @param ignoreKnoraConstraints if `true`, ignores the use of the entity in Knora subject or object constraints. + * @param ignoreRdfSubjectAndObject if `true`, ignores the use of the entity in `rdf:subject` and `rdf:object`. + */ + protected def throwIfEntityIsUsed( + entityIri: SmartIri, + errorFun: => Nothing, + ignoreKnoraConstraints: Boolean = false, + ignoreRdfSubjectAndObject: Boolean = false + ): Future[Unit] = for { entityIsUsed: Boolean <- isEntityUsed(entityIri, ignoreKnoraConstraints, ignoreRdfSubjectAndObject) _ = if (entityIsUsed) { - errorFun - } + errorFun + } } yield () - } /** - * Checks whether an entity with the provided custom IRI exists in the triplestore, if yes, throws an exception. - * If no custom IRI was given, creates a random unused IRI. - * - * @param entityIri the optional custom IRI of the entity. - * @param iriFormatter the stringFormatter method that must be used to create a random Iri. - * @return IRI of the entity. - */ - protected def checkOrCreateEntityIri(entityIri: Option[SmartIri], iriFormatter: => IRI): Future[IRI] = { - + * Checks whether an entity with the provided custom IRI exists in the triplestore, if yes, throws an exception. + * If no custom IRI was given, creates a random unused IRI. + * + * @param entityIri the optional custom IRI of the entity. + * @param iriFormatter the stringFormatter method that must be used to create a random Iri. + * @return IRI of the entity. + */ + protected def checkOrCreateEntityIri(entityIri: Option[SmartIri], iriFormatter: => IRI): Future[IRI] = entityIri match { case Some(customEntityIri: SmartIri) => val entityIriAsString = customEntityIri.toString @@ -181,17 +183,17 @@ abstract class Responder(responderData: ResponderData) extends LazyLogging { result <- stringFormatter.checkIriExists(entityIriAsString, storeManager) _ = if (result) { - throw DuplicateValueException(s"IRI: '$entityIriAsString' already exists, try another one.") - } + throw DuplicateValueException(s"IRI: '$entityIriAsString' already exists, try another one.") + } // Check that given entityIRI ends with a UUID ending: String = entityIriAsString.split('/').last _ = stringFormatter.validateBase64EncodedUuid( - ending, - throw BadRequestException(s"IRI: '$entityIriAsString' must end with a valid base 64 UUID.")) + ending, + throw BadRequestException(s"IRI: '$entityIriAsString' must end with a valid base 64 UUID.") + ) } yield entityIriAsString case None => stringFormatter.makeUnusedIri(iriFormatter, storeManager, loggingAdapter) } - } } 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 fda624a377..9e0966ad18 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 @@ -17,96 +17,66 @@ * License along with Knora. If not, see . */ -package org.knora.webapi -package responders.v2 - -import exceptions._ -import feature.FeatureFactoryConfig -import messages.IriConversions._ -import messages.StringFormatter.{SalsahGuiAttribute, SalsahGuiAttributeDefinition} -import messages.admin.responder.projectsmessages.{ProjectGetRequestADM, ProjectGetResponseADM, ProjectIdentifierADM} -import messages.admin.responder.usersmessages.UserADM -import messages.store.triplestoremessages._ -import messages.util.rdf.{SparqlSelectResult, VariableResultsRow} -import messages.util.{ErrorHandlingMap, KnoraSystemInstances, OntologyUtil, ResponderData} -import messages.v2.responder.{CanDoResponseV2, SuccessResponseV2} -import messages.v2.responder.ontologymessages.Cardinality.KnoraCardinalityInfo -import messages.v2.responder.ontologymessages._ -import messages.v2.responder.standoffmessages.StandoffDataTypeClasses -import messages.{OntologyConstants, SmartIri} -import responders.Responder.handleUnexpectedMessage -import responders.{IriLocker, Responder} -import util._ -import util.cache.CacheUtil +package org.knora.webapi.responders.v2 import akka.http.scaladsl.util.FastFuture import akka.pattern._ +import org.knora.webapi.exceptions._ +import org.knora.webapi.messages.IriConversions._ +import org.knora.webapi.messages.admin.responder.projectsmessages.{ + ProjectGetRequestADM, + ProjectGetResponseADM, + ProjectIdentifierADM +} +import org.knora.webapi.messages.admin.responder.usersmessages.UserADM +import org.knora.webapi.messages.store.triplestoremessages.{ + SmartIriLiteralV2, + SparqlUpdateRequest, + SparqlUpdateResponse, + StringLiteralV2 +} +import org.knora.webapi.messages.util.{ErrorHandlingMap, ResponderData} +import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.KnoraCardinalityInfo +import org.knora.webapi.messages.v2.responder.ontologymessages._ +import org.knora.webapi.messages.v2.responder.{CanDoResponseV2, SuccessResponseV2} +import org.knora.webapi.messages.{OntologyConstants, SmartIri} +import org.knora.webapi.responders.Responder.handleUnexpectedMessage +import org.knora.webapi.responders.v2.ontology.Cache.ONTOLOGY_CACHE_LOCK_IRI +import org.knora.webapi.responders.v2.ontology.{Cache, Cardinalities, OntologyHelpers} +import org.knora.webapi.responders.{IriLocker, Responder} +import org.knora.webapi.util._ +import org.knora.webapi._ import java.time.Instant -import scala.collection.immutable -import scala.concurrent.Future +import scala.concurrent.{ExecutionContext, Future} /** - * Responds to requests dealing with ontologies. - * - * The API v2 ontology responder reads ontologies from two sources: - * - * - The triplestore. - * - The constant knora-api v2 ontologies that are defined in Scala rather than in the triplestore, [[KnoraBaseToApiV2SimpleTransformationRules]] and [[KnoraBaseToApiV2ComplexTransformationRules]]. - * - * It maintains an in-memory cache of all ontology data. This cache can be refreshed by sending a [[LoadOntologiesRequestV2]]. - * - * Read requests to the ontology responder may contain internal or external IRIs as needed. Response messages from the - * ontology responder will contain internal IRIs and definitions, unless a constant API v2 ontology was requested, - * in which case the response will be in the requested API v2 schema. - * - * In API v2, the ontology responder can also create and update ontologies. Update requests must contain - * [[ApiV2Complex]] IRIs and definitions. - * - * The API v1 ontology responder, which is read-only, delegates most of its work to this responder. - */ + * Responds to requests dealing with ontologies. + * + * The API v2 ontology responder reads ontologies from two sources: + * + * - The triplestore. + * - The constant knora-api v2 ontologies that are defined in Scala rather than in the triplestore, [[KnoraBaseToApiV2SimpleTransformationRules]] and [[KnoraBaseToApiV2ComplexTransformationRules]]. + * + * It maintains an in-memory cache of all ontology data. This cache can be refreshed by sending a [[LoadOntologiesRequestV2]]. + * + * Read requests to the ontology responder may contain internal or external IRIs as needed. Response messages from the + * ontology responder will contain internal IRIs and definitions, unless a constant API v2 ontology was requested, + * in which case the response will be in the requested API v2 schema. + * + * In API v2, the ontology responder can also create and update ontologies. Update requests must contain + * [[ApiV2Complex]] IRIs and definitions. + * + * The API v1 ontology responder, which is read-only, delegates most of its work to this responder. + */ class OntologyResponderV2(responderData: ResponderData) extends Responder(responderData) { - // The name of the ontology cache. - private val OntologyCacheName = "ontologyCache" - - // The cache key under which cached ontology data is stored. - private val OntologyCacheKey = "ontologyCacheData" - - // The global ontology cache lock. This is needed because every ontology update replaces the whole ontology cache - // (because definitions in one ontology can refer to definitions in another ontology). Without a global lock, - // concurrent updates (even to different ontologies) could overwrite each other. - private val ONTOLOGY_CACHE_LOCK_IRI = "http://rdfh.ch/ontologies" - - /** - * The in-memory cache of ontologies. - * - * @param ontologies a map of ontology IRIs to ontologies. - * @param subClassOfRelations a map of subclasses to their base classes. - * @param superClassOfRelations a map of base classes to their subclasses. - * @param subPropertyOfRelations a map of subproperties to their base proeprties. - * @param guiAttributeDefinitions a map of salsah-gui:Guielement individuals to their GUI attribute definitions. - * @param standoffProperties a set of standoff properties. - */ - private case class OntologyCacheData(ontologies: Map[SmartIri, ReadOntologyV2], - subClassOfRelations: Map[SmartIri, Seq[SmartIri]], - superClassOfRelations: Map[SmartIri, Set[SmartIri]], - subPropertyOfRelations: Map[SmartIri, Set[SmartIri]], - guiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]], - standoffProperties: Set[SmartIri]) { - lazy val allPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = ontologies.values - .flatMap(_.properties.map { - case (propertyIri, readPropertyInfo) => propertyIri -> readPropertyInfo.entityInfoContent - }) - .toMap - } - /** - * Receives a message of type [[OntologiesResponderRequestV2]], and returns an appropriate response message. - */ + * Receives a message of type [[OntologiesResponderRequestV2]], and returns an appropriate response message. + */ def receive(msg: OntologiesResponderRequestV2) = msg match { case LoadOntologiesRequestV2(featureFactoryConfig, requestingUser) => - loadOntologies(featureFactoryConfig, requestingUser) + Cache.loadOntologies(settings, storeManager, featureFactoryConfig, requestingUser) case EntityInfoGetRequestV2(classIris, propertyIris, requestingUser) => getEntityInfoResponseV2(classIris, propertyIris, requestingUser) case StandoffEntityInfoGetRequestV2(standoffClassIris, standoffPropertyIris, requestingUser) => @@ -143,6 +113,10 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon canChangeClassCardinalities(canChangeCardinalitiesRequest) case changeCardinalitiesRequest: ChangeCardinalitiesRequestV2 => changeClassCardinalities(changeCardinalitiesRequest) + case canDeleteCardinalitiesFromClassRequestV2: CanDeleteCardinalitiesFromClassRequestV2 => + canDeleteCardinalitiesFromClass(canDeleteCardinalitiesFromClassRequestV2) + case deleteCardinalitiesfromClassRequest: DeleteCardinalitiesFromClassRequestV2 => + deleteCardinalitiesFromClass(deleteCardinalitiesfromClassRequest) case changeGuiOrderRequest: ChangeGuiOrderRequestV2 => changeGuiOrder(changeGuiOrderRequest) case canDeleteClassRequest: CanDeleteClassRequestV2 => canDeleteClass(canDeleteClassRequest) case deleteClassRequest: DeleteClassRequestV2 => deleteClass(deleteClassRequest) @@ -159,1969 +133,546 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } /** - * Represents the contents of a named graph representing an ontology. - * - * @param ontologyIri the ontology IRI, which is also the IRI of the named graph. - * @param constructResponse the triplestore's response to a CONSTRUCT query that gets the contents of the named graph. - */ - private case class OntologyGraph(ontologyIri: SmartIri, constructResponse: SparqlExtendedConstructResponse) - - /** - * Loads and caches all ontology information. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[SuccessResponseV2]]. - */ - private def loadOntologies(featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[SuccessResponseV2] = { - val loadOntologiesFuture: Future[SuccessResponseV2] = for { - _ <- Future { - if (!(requestingUser.id == KnoraSystemInstances.Users.SystemUser.id || requestingUser.permissions.isSystemAdmin)) { - throw ForbiddenException(s"Only a system administrator can reload ontologies") - } - } - - // Get all ontology metadata. - allOntologyMetadataSparql <- FastFuture.successful( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getAllOntologyMetadata(triplestore = settings.triplestoreType) - .toString()) - allOntologyMetadataResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(allOntologyMetadataSparql)) - .mapTo[SparqlSelectResult] - allOntologyMetadata: Map[SmartIri, OntologyMetadataV2] = buildOntologyMetadata(allOntologyMetadataResponse) - - knoraBaseOntologyMetadata: OntologyMetadataV2 = allOntologyMetadata.getOrElse( - OntologyConstants.KnoraBase.KnoraBaseOntologyIri.toSmartIri, - throw InconsistentRepositoryDataException(s"No knora-base ontology found")) - knoraBaseOntologyVersion: String = knoraBaseOntologyMetadata.ontologyVersion.getOrElse( - throw InconsistentRepositoryDataException( - "The knora-base ontology in the repository is not up to date. See the Knora documentation on repository updates.")) - - _ = if (knoraBaseOntologyVersion != KnoraBaseVersion) { - throw InconsistentRepositoryDataException( - s"The knora-base ontology in the repository has version '$knoraBaseOntologyVersion', but this version of Knora requires '$KnoraBaseVersion'. See the Knora documentation on repository updates.") - } - - // Get the contents of each named graph containing an ontology. - ontologyGraphResponseFutures: Iterable[Future[OntologyGraph]] = allOntologyMetadata.keys.map { ontologyIri => - val ontologyGraphConstructQuery = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getOntologyGraph( - triplestore = settings.triplestoreType, - ontologyGraph = ontologyIri - ) - .toString - - (storeManager ? SparqlExtendedConstructRequest( - sparql = ontologyGraphConstructQuery, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse].map { response => - OntologyGraph(ontologyIri = ontologyIri, constructResponse = response) - } - } - - ontologyGraphs: Iterable[OntologyGraph] <- Future.sequence(ontologyGraphResponseFutures) - - _ = makeOntologyCache(allOntologyMetadata, ontologyGraphs) - } yield SuccessResponseV2("Ontologies loaded.") - - loadOntologiesFuture.recover { - case exception: Throwable => - exception match { - case inconsistentRepositoryDataException: InconsistentRepositoryDataException => - log.error(inconsistentRepositoryDataException.message) - SuccessResponseV2( - s"An error occurred when loading ontologies: ${inconsistentRepositoryDataException.message}") - - case other => throw other - } - } - } - - /** - * Given ontology metdata and ontology graphs read from the triplestore, constructs the ontology cache. - * - * @param allOntologyMetadata a map of ontology IRIs to ontology metadata. - * @param ontologyGraphs a list of ontology graphs. - */ - private def makeOntologyCache(allOntologyMetadata: Map[SmartIri, OntologyMetadataV2], - ontologyGraphs: Iterable[OntologyGraph]): Unit = { - // Get the IRIs of all the entities in each ontology. - - // A map of ontology IRIs to class IRIs in each ontology. - val classIrisPerOntology: Map[SmartIri, Set[SmartIri]] = getEntityIrisFromOntologyGraphs( - ontologyGraphs = ontologyGraphs, - entityTypes = Set(OntologyConstants.Owl.Class) - ) - - // A map of ontology IRIs to property IRIs in each ontology. - val propertyIrisPerOntology: Map[SmartIri, Set[SmartIri]] = getEntityIrisFromOntologyGraphs( - ontologyGraphs = ontologyGraphs, - entityTypes = Set( - OntologyConstants.Owl.ObjectProperty, - OntologyConstants.Owl.DatatypeProperty, - OntologyConstants.Owl.AnnotationProperty, - OntologyConstants.Rdf.Property - ) - ) - - // A map of ontology IRIs to named individual IRIs in each ontology. - val individualIrisPerOntology: Map[SmartIri, Set[SmartIri]] = getEntityIrisFromOntologyGraphs( - ontologyGraphs = ontologyGraphs, - entityTypes = Set(OntologyConstants.Owl.NamedIndividual) - ) - - // Construct entity definitions. - - // A map of class IRIs to class definitions. - val allClassDefs: Map[SmartIri, ClassInfoContentV2] = ontologyGraphs.flatMap { ontologyGraph => - constructResponseToClassDefinitions( - classIris = classIrisPerOntology(ontologyGraph.ontologyIri), - constructResponse = ontologyGraph.constructResponse - ) - }.toMap - - // A map of property IRIs to property definitions. - val allPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = ontologyGraphs.flatMap { ontologyGraph => - constructResponseToPropertyDefinitions( - propertyIris = propertyIrisPerOntology(ontologyGraph.ontologyIri), - constructResponse = ontologyGraph.constructResponse - ) - }.toMap - - // A map of OWL named individual IRIs to named individuals. - val allIndividuals: Map[SmartIri, IndividualInfoContentV2] = ontologyGraphs.flatMap { ontologyGraph => - constructResponseToIndividuals( - individualIris = individualIrisPerOntology(ontologyGraph.ontologyIri), - constructResponse = ontologyGraph.constructResponse - ) - }.toMap - - // A map of salsah-gui:Guielement individuals to their GUI attribute definitions. - val allGuiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]] = makeGuiAttributeDefinitions( - allIndividuals) - - // Determine relations between entities. - - // A map of class IRIs to their immediate base classes. - val directSubClassOfRelations: Map[SmartIri, Set[SmartIri]] = allClassDefs.map { - case (classIri, classDef) => classIri -> classDef.subClassOf - } - - // A map of property IRIs to their immediate base properties. - val directSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]] = allPropertyDefs.map { - case (propertyIri, propertyDef) => propertyIri -> propertyDef.subPropertyOf - } - - val allClassIris = allClassDefs.keySet - val allPropertyIris = allPropertyDefs.keySet - - // A map in which each class IRI points to the full sequence of its base classes. - val allSubClassOfRelations: Map[SmartIri, Seq[SmartIri]] = allClassIris.toSeq.map { classIri => - // get the hierarchically ordered base classes. - val baseClasses: Seq[SmartIri] = OntologyUtil.getAllBaseDefs(classIri, directSubClassOfRelations) - // prepend the classIri to the sequence of base classes because a class is also a subclass of itself. - (classIri, classIri +: baseClasses) - }.toMap - - // A map in which each class IRI points to the full set of its subclasses. A class is also - // a subclass of itself. - val allSuperClassOfRelations: Map[SmartIri, Set[SmartIri]] = calculateSuperClassOfRelations(allSubClassOfRelations) - - // Make a map in which each property IRI points to the full set of its base properties. A property is also - // a subproperty of itself. - val allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]] = allPropertyIris.map { propertyIri => - (propertyIri, OntologyUtil.getAllBaseDefs(propertyIri, directSubPropertyOfRelations).toSet + propertyIri) - }.toMap - - // A set of all subproperties of knora-base:resourceProperty. - val allKnoraResourceProps: Set[SmartIri] = allPropertyIris.filter { prop => - val allPropSubPropertyOfRelations = allSubPropertyOfRelations(prop) - prop == OntologyConstants.KnoraBase.ResourceProperty.toSmartIri || - allPropSubPropertyOfRelations.contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) || - allPropSubPropertyOfRelations.contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri) - } - - // A set of all subproperties of knora-base:hasLinkTo. - val allLinkProps: Set[SmartIri] = allPropertyIris.filter(prop => - allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri)) - - // A set of all subproperties of knora-base:hasLinkToValue. - val allLinkValueProps: Set[SmartIri] = allPropertyIris.filter(prop => - allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri)) - - // A set of all subproperties of knora-base:hasFileValue. - val allFileValueProps: Set[SmartIri] = allPropertyIris.filter(prop => - allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasFileValue.toSmartIri)) - - // A map of the cardinalities defined directly on each resource class. Each class IRI points to a map of - // property IRIs to KnoraCardinalityInfo objects. - val directClassCardinalities: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] = allClassDefs.map { - case (classIri, classDef) => - classIri -> classDef.directCardinalities - } - - // Allow each class to inherit cardinalities from its base classes. - val classCardinalitiesWithInheritance: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] = allClassIris.map { - resourceClassIri => - val resourceClassCardinalities: Map[SmartIri, KnoraCardinalityInfo] = inheritCardinalitiesInLoadedClass( - classIri = resourceClassIri, - directSubClassOfRelations = directSubClassOfRelations, - allSubPropertyOfRelations = allSubPropertyOfRelations, - directClassCardinalities = directClassCardinalities - ) - - resourceClassIri -> resourceClassCardinalities - }.toMap - - // Construct a ReadClassInfoV2 for each class. - val readClassInfos: Map[SmartIri, ReadClassInfoV2] = makeReadClassInfos( - classDefs = allClassDefs, - directClassCardinalities = directClassCardinalities, - classCardinalitiesWithInheritance = classCardinalitiesWithInheritance, - directSubClassOfRelations = directSubClassOfRelations, - allSubClassOfRelations = allSubClassOfRelations, - allSubPropertyOfRelations = allSubPropertyOfRelations, - allPropertyDefs = allPropertyDefs, - allKnoraResourceProps = allKnoraResourceProps, - allLinkProps = allLinkProps, - allLinkValueProps = allLinkValueProps, - allFileValueProps = allFileValueProps - ) - - // Construct a ReadPropertyInfoV2 for each property definition. - val readPropertyInfos: Map[SmartIri, ReadPropertyInfoV2] = makeReadPropertyInfos( - propertyDefs = allPropertyDefs, - directSubPropertyOfRelations = directSubPropertyOfRelations, - allSubPropertyOfRelations = allSubPropertyOfRelations, - allSubClassOfRelations = allSubClassOfRelations, - allGuiAttributeDefinitions = allGuiAttributeDefinitions, - allKnoraResourceProps = allKnoraResourceProps, - allLinkProps = allLinkProps, - allLinkValueProps = allLinkValueProps, - allFileValueProps = allFileValueProps - ) - - // Construct a ReadIndividualV2 for each OWL named individual. - val readIndividualInfos = makeReadIndividualInfos(allIndividuals) - - // A ReadOntologyV2 for each ontology to be cached. - val readOntologies: Map[SmartIri, ReadOntologyV2] = allOntologyMetadata.map { - case (ontologyIri, ontologyMetadata) => - ontologyIri -> ReadOntologyV2( - ontologyMetadata = ontologyMetadata, - classes = readClassInfos.filter { - case (classIri, _) => classIri.getOntologyFromEntity == ontologyIri - }, - properties = readPropertyInfos.filter { - case (propertyIri, _) => propertyIri.getOntologyFromEntity == ontologyIri - }, - individuals = readIndividualInfos.filter { - case (individualIri, _) => individualIri.getOntologyFromEntity == ontologyIri - }, - isWholeOntology = true - ) - } - - // A set of the IRIs of all properties used in cardinalities in standoff classes. - val propertiesUsedInStandoffCardinalities: Set[SmartIri] = readClassInfos.flatMap { - case (_, readClassInfo) => - if (readClassInfo.isStandoffClass) { - readClassInfo.allCardinalities.keySet - } else { - Set.empty[SmartIri] - } - }.toSet - - // A set of the IRIs of all properties whose subject class constraint is a standoff class. - val propertiesWithStandoffTagSubjects: Set[SmartIri] = readPropertyInfos.flatMap { - case (propertyIri, readPropertyInfo) => - readPropertyInfo.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { - case Some(subjectClassConstraint: SmartIri) => - readClassInfos.get(subjectClassConstraint) match { - case Some(subjectReadClassInfo: ReadClassInfoV2) => - if (subjectReadClassInfo.isStandoffClass) { - Some(propertyIri) - } else { - None - } - - case None => None - } - - case None => None - } - }.toSet - - // Construct the ontology cache data. - val ontologyCacheData: OntologyCacheData = OntologyCacheData( - ontologies = new ErrorHandlingMap[SmartIri, ReadOntologyV2](readOntologies, { key => - s"Ontology not found: $key" - }), - subClassOfRelations = new ErrorHandlingMap[SmartIri, Seq[SmartIri]](allSubClassOfRelations, { key => - s"Class not found: $key" - }), - superClassOfRelations = new ErrorHandlingMap[SmartIri, Set[SmartIri]](allSuperClassOfRelations, { key => - s"Class not found: $key" - }), - subPropertyOfRelations = new ErrorHandlingMap[SmartIri, Set[SmartIri]](allSubPropertyOfRelations, { key => - s"Property not found: $key" - }), - guiAttributeDefinitions = - new ErrorHandlingMap[SmartIri, Set[SalsahGuiAttributeDefinition]](allGuiAttributeDefinitions, { key => - s"salsah-gui:Guielement not found: $key" - }), - standoffProperties = propertiesUsedInStandoffCardinalities ++ propertiesWithStandoffTagSubjects - ) - - // Check property subject and object class constraints. - - readPropertyInfos.foreach { - case (propertyIri, readPropertyInfo) => - val allSuperPropertyIris: Set[SmartIri] = allSubPropertyOfRelations.getOrElse(propertyIri, Set.empty[SmartIri]) - - readPropertyInfo.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { - case Some(subjectClassConstraint) => - // Each property's subject class constraint, if provided, must be a subclass of the subject class constraints of all its base properties. - checkPropertyConstraint( - cacheData = ontologyCacheData, - internalPropertyIri = propertyIri, - constraintPredicateIri = OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri, - constraintValueToBeChecked = subjectClassConstraint, - allSuperPropertyIris = allSuperPropertyIris, - errorSchema = InternalSchema, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) - - // If the property is defined in a project-specific ontology, its subject class constraint, if provided, must be a Knora resource or standoff class. - if (!propertyIri.isKnoraBuiltInDefinitionIri) { - val baseClassesOfSubjectClassConstraint = allSubClassOfRelations(subjectClassConstraint) - - if (!(baseClassesOfSubjectClassConstraint.contains(OntologyConstants.KnoraBase.Resource.toSmartIri) || - baseClassesOfSubjectClassConstraint.contains(OntologyConstants.KnoraBase.StandoffTag.toSmartIri))) { - throw InconsistentRepositoryDataException( - s"Property $propertyIri is defined in a project-specific ontology, but its knora-base:subjectClassConstraint, $subjectClassConstraint, is not a subclass of knora-base:Resource or knora-base:StandoffTag") - } - } - - case None => () - } - - readPropertyInfo.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri) match { - case Some(objectClassConstraint) => - // Each property's object class constraint, if provided, must be a subclass of the object class constraints of all its base properties. - checkPropertyConstraint( - cacheData = ontologyCacheData, - internalPropertyIri = propertyIri, - constraintPredicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, - constraintValueToBeChecked = objectClassConstraint, - allSuperPropertyIris = allSuperPropertyIris, - errorSchema = InternalSchema, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) - - case None => - // A resource property must have an object class constraint, unless it's knora-base:resourceProperty. - if (readPropertyInfo.isResourceProp && propertyIri != OntologyConstants.KnoraBase.ResourceProperty.toSmartIri) { - throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint") - } - } - } - - // Check references between ontologies. - checkReferencesBetweenOntologies(ontologyCacheData) - - // Update the cache. - storeCacheData(ontologyCacheData) - } - - /** - * Checks a reference between an ontology entity and another ontology entity to see if the target - * is in a non-shared ontology in another project. - * - * @param ontologyCacheData the ontology cache data. - * @param sourceEntityIri the entity whose definition contains the reference. - * @param targetEntityIri the entity that's the target of the reference. - * @param errorFun a function that throws an exception with the specified message if the reference is invalid. - */ - private def checkOntologyReferenceInEntity(ontologyCacheData: OntologyCacheData, - sourceEntityIri: SmartIri, - targetEntityIri: SmartIri, - errorFun: String => Nothing): Unit = { - if (targetEntityIri.isKnoraDefinitionIri) { - val sourceOntologyIri = sourceEntityIri.getOntologyFromEntity - val sourceOntologyMetadata = ontologyCacheData.ontologies(sourceOntologyIri).ontologyMetadata - - val targetOntologyIri = targetEntityIri.getOntologyFromEntity - val targetOntologyMetadata = ontologyCacheData.ontologies(targetOntologyIri).ontologyMetadata - - if (sourceOntologyMetadata.projectIri != targetOntologyMetadata.projectIri) { - if (!(targetOntologyIri.isKnoraBuiltInDefinitionIri || targetOntologyIri.isKnoraSharedDefinitionIri)) { - errorFun( - s"Entity $sourceEntityIri refers to entity $targetEntityIri, which is in a non-shared ontology that belongs to another project") - } - } - } - } - - /** - * Checks a property definition to ensure that it doesn't refer to any other non-shared ontologies. - * - * @param ontologyCacheData the ontology cache data. - * @param propertyDef the property definition. - * @param errorFun a function that throws an exception with the specified message if the property definition is invalid. - */ - private def checkOntologyReferencesInPropertyDef(ontologyCacheData: OntologyCacheData, - propertyDef: PropertyInfoContentV2, - errorFun: String => Nothing): Unit = { - // Ensure that the property isn't a subproperty of any property in a non-shared ontology in another project. - - for (subPropertyOf <- propertyDef.subPropertyOf) { - checkOntologyReferenceInEntity( - ontologyCacheData = ontologyCacheData, - sourceEntityIri = propertyDef.propertyIri, - targetEntityIri = subPropertyOf, - errorFun = errorFun - ) - } - - // Ensure that the property doesn't have subject or object constraints pointing to a non-shared ontology in another project. - - propertyDef.getPredicateIriObject(OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { - case Some(subjectClassConstraint) => - checkOntologyReferenceInEntity( - ontologyCacheData = ontologyCacheData, - sourceEntityIri = propertyDef.propertyIri, - targetEntityIri = subjectClassConstraint, - errorFun = errorFun - ) - - case None => () - } - - propertyDef.getPredicateIriObject(OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri) match { - case Some(objectClassConstraint) => - checkOntologyReferenceInEntity( - ontologyCacheData = ontologyCacheData, - sourceEntityIri = propertyDef.propertyIri, - targetEntityIri = objectClassConstraint, - errorFun = errorFun - ) - - case None => () - } - } - - /** - * Checks a class definition to ensure that it doesn't refer to any non-shared ontologies in other projects. - * - * @param ontologyCacheData the ontology cache data. - * @param classDef the class definition. - * @param errorFun a function that throws an exception with the specified message if the property definition is invalid. - */ - private def checkOntologyReferencesInClassDef(ontologyCacheData: OntologyCacheData, - classDef: ClassInfoContentV2, - errorFun: String => Nothing): Unit = { - for (subClassOf <- classDef.subClassOf) { - checkOntologyReferenceInEntity( - ontologyCacheData = ontologyCacheData, - sourceEntityIri = classDef.classIri, - targetEntityIri = subClassOf, - errorFun = errorFun - ) - } - - for (cardinalityPropIri <- classDef.directCardinalities.keys) { - checkOntologyReferenceInEntity( - ontologyCacheData = ontologyCacheData, - sourceEntityIri = classDef.classIri, - targetEntityIri = cardinalityPropIri, - errorFun = errorFun - ) - } - } - - /** - * Checks references between ontologies to ensure that they do not refer to non-shared ontologies in other projects. - * - * @param ontologyCacheData the ontology cache data. - */ - private def checkReferencesBetweenOntologies(ontologyCacheData: OntologyCacheData): Unit = { - for (ontology <- ontologyCacheData.ontologies.values) { - for (propertyInfo <- ontology.properties.values) { - checkOntologyReferencesInPropertyDef( - ontologyCacheData = ontologyCacheData, - propertyDef = propertyInfo.entityInfoContent, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) - } - - for (classInfo <- ontology.classes.values) { - checkOntologyReferencesInClassDef( - ontologyCacheData = ontologyCacheData, - classDef = classInfo.entityInfoContent, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) - } - } - } - - /** - * Given a list of ontology graphs, finds the IRIs of all subjects whose `rdf:type` is contained in a given set of types. - * - * @param ontologyGraphs a list of ontology graphs. - * @param entityTypes the types of entities to be found. - * @return a map of ontology IRIs to sets of the IRIs of entities with matching types in each ontology. - */ - private def getEntityIrisFromOntologyGraphs(ontologyGraphs: Iterable[OntologyGraph], - entityTypes: Set[IRI]): Map[SmartIri, Set[SmartIri]] = { - val entityTypesAsIriLiterals = entityTypes.map(entityType => IriLiteralV2(entityType)) - - ontologyGraphs.map { ontologyGraph => - val entityIrisInGraph: Set[SmartIri] = ontologyGraph.constructResponse.statements.foldLeft(Set.empty[SmartIri]) { - case (acc, (subjectIri: IriSubjectV2, subjectStatements: Map[SmartIri, Seq[LiteralV2]])) => - val subjectTypeLiterals: Seq[IriLiteralV2] = subjectStatements - .getOrElse(OntologyConstants.Rdf.Type.toSmartIri, - throw InconsistentRepositoryDataException(s"Subject $subjectIri has no rdf:type")) - .collect { - case iriLiteral: IriLiteralV2 => iriLiteral - } - - if (subjectTypeLiterals.exists(entityTypesAsIriLiterals.contains)) { - acc + subjectIri.value.toSmartIri - } else { - acc - } - - case (acc, _) => acc - } - - ontologyGraph.ontologyIri -> entityIrisInGraph - }.toMap - } - - /** - * Given the triplestore's response to `getAllOntologyMetadata.scala.txt`, constructs a map of ontology IRIs - * to ontology metadata for the ontology cache. - * - * @param allOntologyMetadataResponse the triplestore's response to the SPARQL query `getAllOntologyMetadata.scala.txt`. - * @return a map of ontology IRIs to ontology metadata. - */ - private def buildOntologyMetadata( - allOntologyMetadataResponse: SparqlSelectResult): Map[SmartIri, OntologyMetadataV2] = { - allOntologyMetadataResponse.results.bindings.groupBy(_.rowMap("ontologyGraph")).map { - case (ontologyGraph: IRI, rows: Seq[VariableResultsRow]) => - val ontologyIri = rows.head.rowMap("ontologyIri") - - if (ontologyIri != ontologyGraph) { - throw InconsistentRepositoryDataException( - s"Ontology $ontologyIri must be stored in named graph $ontologyIri, but it is in $ontologyGraph") - } - - val ontologySmartIri = ontologyIri.toSmartIri - - if (!ontologySmartIri.isKnoraOntologyIri) { - throw InconsistentRepositoryDataException(s"Ontology $ontologySmartIri is not a Knora ontology") - } - - val ontologyMetadataMap: Map[IRI, String] = rows.map { row => - val pred = - row.rowMap.getOrElse("ontologyPred", - throw InconsistentRepositoryDataException(s"Empty predicate in ontology $ontologyIri")) - val obj = row.rowMap.getOrElse( - "ontologyObj", - throw InconsistentRepositoryDataException(s"Empty object for predicate $pred in ontology $ontologyIri")) - pred -> obj - }.toMap - - val projectIri: SmartIri = ontologyMetadataMap - .getOrElse( - OntologyConstants.KnoraBase.AttachedToProject, - throw InconsistentRepositoryDataException(s"Ontology $ontologyIri has no knora-base:attachedToProject")) - .toSmartIri - val ontologyLabel: String = - ontologyMetadataMap.getOrElse(OntologyConstants.Rdfs.Label, ontologySmartIri.getOntologyName) - val lastModificationDate: Option[Instant] = ontologyMetadataMap - .get(OntologyConstants.KnoraBase.LastModificationDate) - .map( - instant => - stringFormatter.xsdDateTimeStampToInstant( - instant, - throw InconsistentRepositoryDataException(s"Invalid UTC instant: $instant"))) - val ontologyVersion: Option[String] = ontologyMetadataMap.get(OntologyConstants.KnoraBase.OntologyVersion) - - ontologySmartIri -> OntologyMetadataV2( - ontologyIri = ontologySmartIri, - projectIri = Some(projectIri), - label = Some(ontologyLabel), - lastModificationDate = lastModificationDate, - ontologyVersion = ontologyVersion - ) - } - } - - /** - * Constructs a map of class IRIs to [[ReadClassInfoV2]] instances, based on class definitions loaded from the - * triplestore. - * - * @param classDefs a map of class IRIs to class definitions. - * @param directClassCardinalities a map of the cardinalities defined directly on each class. Each resource class - * IRI points to a map of property IRIs to [[KnoraCardinalityInfo]] objects. - * @param classCardinalitiesWithInheritance a map of the cardinalities defined directly on each class or inherited from - * base classes. Each class IRI points to a map of property IRIs to - * [[KnoraCardinalityInfo]] objects. - * @param directSubClassOfRelations a map of class IRIs to their immediate base classes. - * @param allSubClassOfRelations a map of class IRIs to all their base classes. - * @param allSubPropertyOfRelations a map of property IRIs to all their base properties. - * @param allPropertyDefs a map of property IRIs to property definitions. - * @param allKnoraResourceProps a set of the IRIs of all Knora resource properties. - * @param allLinkProps a set of the IRIs of all link properties. - * @param allLinkValueProps a set of the IRIs of link value properties. - * @param allFileValueProps a set of the IRIs of all file value properties. - * @return a map of resource class IRIs to their definitions. - */ - private def makeReadClassInfos(classDefs: Map[SmartIri, ClassInfoContentV2], - directClassCardinalities: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]], - classCardinalitiesWithInheritance: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]], - directSubClassOfRelations: Map[SmartIri, Set[SmartIri]], - allSubClassOfRelations: Map[SmartIri, Seq[SmartIri]], - allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], - allPropertyDefs: Map[SmartIri, PropertyInfoContentV2], - allKnoraResourceProps: Set[SmartIri], - allLinkProps: Set[SmartIri], - allLinkValueProps: Set[SmartIri], - allFileValueProps: Set[SmartIri]): Map[SmartIri, ReadClassInfoV2] = { - classDefs.map { - case (classIri, classDef) => - val ontologyIri = classIri.getOntologyFromEntity - - // Get the OWL cardinalities for the class. - val allOwlCardinalitiesForClass: Map[SmartIri, KnoraCardinalityInfo] = - classCardinalitiesWithInheritance(classIri) - val allPropertyIrisForCardinalitiesInClass: Set[SmartIri] = allOwlCardinalitiesForClass.keys.toSet - - // Identify the Knora resource properties, link properties, link value properties, and file value properties in the cardinalities. - val knoraResourcePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allKnoraResourceProps) - val linkPropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allLinkProps) - val linkValuePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allLinkValueProps) - val fileValuePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allFileValueProps) - - // Make sure there is a link value property for each link property. - - val missingLinkValueProps = linkPropsInClass.map(_.fromLinkPropToLinkValueProp) -- linkValuePropsInClass - - if (missingLinkValueProps.nonEmpty) { - throw InconsistentRepositoryDataException( - s"Resource class $classIri has cardinalities for one or more link properties without corresponding link value properties. The missing (or incorrectly defined) property or properties: ${missingLinkValueProps - .mkString(", ")}") - } - - // Make sure there is a link property for each link value property. - - val missingLinkProps = linkValuePropsInClass.map(_.fromLinkValuePropToLinkProp) -- linkPropsInClass - - if (missingLinkProps.nonEmpty) { - throw InconsistentRepositoryDataException( - s"Resource class $classIri has cardinalities for one or more link value properties without corresponding link properties. The missing (or incorrectly defined) property or properties: ${missingLinkProps - .mkString(", ")}") - } - - // Make sure that the cardinality for each link property is the same as the cardinality for the corresponding link value property. - for (linkProp <- linkPropsInClass) { - val linkValueProp: SmartIri = linkProp.fromLinkPropToLinkValueProp - val linkPropCardinality: KnoraCardinalityInfo = allOwlCardinalitiesForClass(linkProp) - val linkValuePropCardinality: KnoraCardinalityInfo = allOwlCardinalitiesForClass(linkValueProp) - - if (!linkPropCardinality.equalsWithoutGuiOrder(linkValuePropCardinality)) { - throw InconsistentRepositoryDataException( - s"In class $classIri, the cardinality for $linkProp is different from the cardinality for $linkValueProp") - } - } - - // The class's direct cardinalities. - val directCardinalities: Map[SmartIri, KnoraCardinalityInfo] = directClassCardinalities(classIri) - - val directCardinalityPropertyIris = directCardinalities.keySet - val allBaseClasses: Seq[SmartIri] = allSubClassOfRelations(classIri) - val isKnoraResourceClass = allBaseClasses.contains(OntologyConstants.KnoraBase.Resource.toSmartIri) - val isStandoffClass = allBaseClasses.contains(OntologyConstants.KnoraBase.StandoffTag.toSmartIri) - val isValueClass = !(isKnoraResourceClass || isStandoffClass) && allBaseClasses.contains( - OntologyConstants.KnoraBase.Value.toSmartIri) - - // If the class is defined in project-specific ontology, do the following checks. - if (!ontologyIri.isKnoraBuiltInDefinitionIri) { - // It must be either a resource class or a standoff class, but not both. - if (!(isKnoraResourceClass ^ isStandoffClass)) { - throw InconsistentRepositoryDataException( - s"Class $classIri must be a subclass either of knora-base:Resource or of knora-base:StandoffTag (but not both)") - } - - // All its cardinalities must be on properties that are defined. - val cardinalitiesOnMissingProps = directCardinalityPropertyIris.filterNot(allPropertyDefs.keySet) - - if (cardinalitiesOnMissingProps.nonEmpty) { - throw InconsistentRepositoryDataException( - s"Class $classIri has one or more cardinalities on undefined properties: ${cardinalitiesOnMissingProps - .mkString(", ")}") - } - - // It cannot have cardinalities both on property P and on a subproperty of P. - - val maybePropertyAndSubproperty: Option[(SmartIri, SmartIri)] = findPropertyAndSubproperty( - propertyIris = allPropertyIrisForCardinalitiesInClass, - subPropertyOfRelations = allSubPropertyOfRelations - ) - - maybePropertyAndSubproperty match { - case Some((basePropertyIri, propertyIri)) => - throw InconsistentRepositoryDataException( - s"Class $classIri has a cardinality on property $basePropertyIri and on its subproperty $propertyIri") - - case None => () - } - - if (isKnoraResourceClass) { - // If it's a resource class, all its directly defined cardinalities must be on Knora resource properties, not including knora-base:resourceProperty or knora-base:hasValue. - - val cardinalitiesOnInvalidProps = directCardinalityPropertyIris.filterNot(allKnoraResourceProps) - - if (cardinalitiesOnInvalidProps.nonEmpty) { - throw InconsistentRepositoryDataException( - s"Resource class $classIri has one or more cardinalities on properties that are not Knora resource properties: ${cardinalitiesOnInvalidProps - .mkString(", ")}") - } - - Set(OntologyConstants.KnoraBase.ResourceProperty, OntologyConstants.KnoraBase.HasValue).foreach { - invalidProp => - if (directCardinalityPropertyIris.contains(invalidProp.toSmartIri)) { - throw InconsistentRepositoryDataException( - s"Class $classIri has a cardinality on property $invalidProp, which is not allowed") - } - } - - // Check for invalid cardinalities on boolean properties. - checkForInvalidBooleanCardinalities( - classIri = classIri, - directCardinalities = directCardinalities, - allPropertyDefs = allPropertyDefs, - schemaForErrors = InternalSchema, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) - - // All its base classes with Knora IRIs must also be resource classes. - for (baseClass <- classDef.subClassOf) { - if (baseClass.isKnoraDefinitionIri && !allSubClassOfRelations(baseClass).contains( - OntologyConstants.KnoraBase.Resource.toSmartIri)) { - throw InconsistentRepositoryDataException( - s"Class $classIri is a subclass of knora-base:Resource, but its base class $baseClass is not") - } - } - - // It must have an rdfs:label. - if (!classDef.predicates.contains(OntologyConstants.Rdfs.Label.toSmartIri)) { - throw InconsistentRepositoryDataException(s"Class $classIri has no rdfs:label") - } - } else { - // If it's a standoff class, none of its cardinalities must be on Knora resource properties. - - val cardinalitiesOnInvalidProps = directCardinalityPropertyIris.filter(allKnoraResourceProps) - - if (cardinalitiesOnInvalidProps.nonEmpty) { - throw InconsistentRepositoryDataException( - s"Standoff class $classIri has one or more cardinalities on properties that are Knora resource properties: ${cardinalitiesOnInvalidProps - .mkString(", ")}") - } - - // All its base classes with Knora IRIs must also be standoff classes. - for (baseClass <- classDef.subClassOf) { - if (baseClass.isKnoraDefinitionIri) { - if (isStandoffClass && !allSubClassOfRelations(baseClass).contains( - OntologyConstants.KnoraBase.StandoffTag.toSmartIri)) { - throw InconsistentRepositoryDataException( - s"Class $classIri is a subclass of knora-base:StandoffTag, but its base class $baseClass is not") - } - } - } - } - } - - // Each class must be a subclass of all the classes that are subject class constraints of the properties in its cardinalities. - checkSubjectClassConstraintsViaCardinalities( - internalClassDef = classDef, - allBaseClassIris = allBaseClasses.toSet, - allClassCardinalityKnoraPropertyDefs = - allPropertyDefs.view.filterKeys(allOwlCardinalitiesForClass.keySet).toMap, - errorSchema = InternalSchema, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) - - val inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = allOwlCardinalitiesForClass - .filterNot { - case (propertyIri, _) => directCardinalityPropertyIris.contains(propertyIri) - } - - // Get the class's standoff data type, if any. A standoff class that has a datatype is a subclass of one of the classes - // in StandoffDataTypeClasses. - - val standoffDataType: Set[SmartIri] = allSubClassOfRelations(classIri).toSet - .intersect(StandoffDataTypeClasses.getStandoffClassIris.map(_.toSmartIri)) - - if (standoffDataType.size > 1) { - throw InconsistentRepositoryDataException( - s"Class $classIri is a subclass of more than one standoff datatype: ${standoffDataType.mkString(", ")}") - } - - // A class can be instantiated if it's in a built-in ontology and marked with knora-base:canBeInstantiated, or if it's - // a resource class in a project-specific ontology. - val canBeInstantiated = if (ontologyIri.isKnoraBuiltInDefinitionIri) { - classDef.predicates - .get(OntologyConstants.KnoraBase.CanBeInstantiated.toSmartIri) - .flatMap(_.objects.headOption) match { - case Some(booleanLiteral: BooleanLiteralV2) => booleanLiteral.value - case _ => false - } - } else { - isKnoraResourceClass - } - - val readClassInfo = ReadClassInfoV2( - entityInfoContent = classDef, - allBaseClasses = allBaseClasses, - isResourceClass = isKnoraResourceClass, - isStandoffClass = isStandoffClass, - isValueClass = isValueClass, - canBeInstantiated = canBeInstantiated, - inheritedCardinalities = inheritedCardinalities, - knoraResourceProperties = knoraResourcePropsInClass, - linkProperties = linkPropsInClass, - linkValueProperties = linkValuePropsInClass, - fileValueProperties = fileValuePropsInClass, - standoffDataType = standoffDataType.headOption match { - case Some(dataType: SmartIri) => - Some( - StandoffDataTypeClasses.lookup( - dataType.toString, - throw InconsistentRepositoryDataException(s"$dataType is not a valid standoff datatype"))) - - case None => None - } - ) - - classIri -> readClassInfo - } - } - - /** - * Checks for invalid cardinalities on boolean properties. - * - * @param classIri the class IRI. - * @param directCardinalities the cardinalities directly defined on the class. - * @param allPropertyDefs all property definitions. - */ - def checkForInvalidBooleanCardinalities(classIri: SmartIri, - directCardinalities: Map[SmartIri, KnoraCardinalityInfo], - allPropertyDefs: Map[SmartIri, PropertyInfoContentV2], - schemaForErrors: OntologySchema, - errorFun: String => Nothing): Unit = { - // A cardinality on a property with a boolean object must be 1 or 0-1. - - val invalidCardinalitiesOnBooleanProps: Set[SmartIri] = directCardinalities.filter { - case (propertyIri, knoraCardinalityInfo) => - val objectClassConstraintIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri - - val propertyObjectClassConstraint: SmartIri = allPropertyDefs(propertyIri).requireIriObject( - objectClassConstraintIri, - errorFun(s"Property ${propertyIri - .toOntologySchema(schemaForErrors)} has no ${objectClassConstraintIri.toOntologySchema(schemaForErrors)}") - ) - - propertyObjectClassConstraint == OntologyConstants.KnoraBase.BooleanValue.toSmartIri && - !(knoraCardinalityInfo.cardinality == Cardinality.MustHaveOne || knoraCardinalityInfo.cardinality == Cardinality.MayHaveOne) - }.keySet - - if (invalidCardinalitiesOnBooleanProps.nonEmpty) { - errorFun( - s"Class ${classIri.toOntologySchema(schemaForErrors).toSparql} has one or more invalid cardinalities on boolean properties: ${invalidCardinalitiesOnBooleanProps - .map(_.toOntologySchema(schemaForErrors).toSparql) - .mkString(", ")}") - } - } - - /** - * Constructs a map of property IRIs to [[ReadPropertyInfoV2]] instances, based on property definitions loaded from the - * triplestore. - * - * @param propertyDefs a map of property IRIs to property definitions. - * @param directSubPropertyOfRelations a map of property IRIs to their immediate base properties. - * @param allSubPropertyOfRelations a map of property IRIs to all their base properties. - * @param allSubClassOfRelations a map of class IRIs to all their base classes. - * @param allGuiAttributeDefinitions a map of `Guielement` IRIs to sets of [[SalsahGuiAttributeDefinition]]. - * @param allKnoraResourceProps a set of the IRIs of all Knora resource properties. - * @param allLinkProps a set of the IRIs of all link properties. - * @param allLinkValueProps a set of the IRIs of link value properties. - * @param allFileValueProps a set of the IRIs of all file value properties. - * @return a map of property IRIs to [[ReadPropertyInfoV2]] instances. - */ - private def makeReadPropertyInfos(propertyDefs: Map[SmartIri, PropertyInfoContentV2], - directSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], - allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], - allSubClassOfRelations: Map[SmartIri, Seq[SmartIri]], - allGuiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]], - allKnoraResourceProps: Set[SmartIri], - allLinkProps: Set[SmartIri], - allLinkValueProps: Set[SmartIri], - allFileValueProps: Set[SmartIri]): Map[SmartIri, ReadPropertyInfoV2] = { - propertyDefs.map { - case (propertyIri, propertyDef) => - val ontologyIri = propertyIri.getOntologyFromEntity - - validateGuiAttributes( - propertyInfoContent = propertyDef, - allGuiAttributeDefinitions = allGuiAttributeDefinitions, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) - - val isResourceProp = allKnoraResourceProps.contains(propertyIri) - val isValueProp = - allSubPropertyOfRelations(propertyIri).contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) - val isLinkProp = allLinkProps.contains(propertyIri) - val isLinkValueProp = allLinkValueProps.contains(propertyIri) - val isFileValueProp = allFileValueProps.contains(propertyIri) - - // If the property is defined in a project-specific ontology and is a Knora resource property (a subproperty of knora-base:hasValue or knora-base:hasLinkTo), do the following checks. - if (!propertyIri.isKnoraBuiltInDefinitionIri && isResourceProp) { - // The property must be a subproperty of knora-base:hasValue or knora-base:hasLinkTo, but not both. - if (isValueProp && isLinkProp) { - throw InconsistentRepositoryDataException( - s"Property $propertyIri cannot be a subproperty of both knora-base:hasValue and knora-base:hasLinkTo") - } - - // It can't be a subproperty of knora-base:hasFileValue. - if (isFileValueProp) { - throw InconsistentRepositoryDataException( - s"Property $propertyIri cannot be a subproperty of knora-base:hasFileValue") - } - - // Each of its base properties that has a Knora IRI must also be a Knora resource property. - for (baseProperty <- propertyDef.subPropertyOf) { - if (baseProperty.isKnoraDefinitionIri && !allKnoraResourceProps.contains(baseProperty)) { - throw InconsistentRepositoryDataException( - s"Property $propertyIri is a subproperty of knora-base:hasValue or knora-base:hasLinkTo, but its base property $baseProperty is not") - } - } - - // It must have an rdfs:label. - if (!propertyDef.predicates.contains(OntologyConstants.Rdfs.Label.toSmartIri)) { - throw InconsistentRepositoryDataException(s"Property $propertyIri has no rdfs:label") - } - } - - // A property is editable if it's in a built-in ontology and marked with knora-base:isEditable, - // or if it's a resource property in a project-specific ontology. - val isEditable = if (ontologyIri.isKnoraBuiltInDefinitionIri) { - propertyDef.predicates - .get(OntologyConstants.KnoraBase.IsEditable.toSmartIri) - .flatMap(_.objects.headOption) match { - case Some(booleanLiteral: BooleanLiteralV2) => booleanLiteral.value - case _ => false - } - } else { - isResourceProp - } - - val propertyEntityInfo = ReadPropertyInfoV2( - entityInfoContent = propertyDef, - isResourceProp = isResourceProp, - isEditable = isEditable, - isLinkProp = isLinkProp, - isLinkValueProp = isLinkValueProp, - isFileValueProp = isFileValueProp, - isStandoffInternalReferenceProperty = allSubPropertyOfRelations(propertyIri).contains( - OntologyConstants.KnoraBase.StandoffTagHasInternalReference.toSmartIri) - ) - - propertyIri -> propertyEntityInfo - } - } - - /** - * Constructs a map of OWL named individual IRIs to [[ReadIndividualInfoV2]] instances. - * - * @param individualDefs a map of OWL named individual IRIs to named individuals. - * @return a map of individual IRIs to [[ReadIndividualInfoV2]] instances. - */ - private def makeReadIndividualInfos( - individualDefs: Map[SmartIri, IndividualInfoContentV2]): Map[SmartIri, ReadIndividualInfoV2] = { - individualDefs.map { - case (individualIri, individual) => - individualIri -> ReadIndividualInfoV2(individual) - } - } - - /** - * Given all the OWL named individuals available, constructs a map of `salsah-gui:Guielement` individuals to - * their GUI attribute definitions. - * - * @param allIndividuals all the OWL named individuals available. - * @return a map of `salsah-gui:Guielement` individuals to their GUI attribute definitions. - */ - private def makeGuiAttributeDefinitions( - allIndividuals: Map[SmartIri, IndividualInfoContentV2]): Map[SmartIri, Set[SalsahGuiAttributeDefinition]] = { - val guiElementIndividuals: Map[SmartIri, IndividualInfoContentV2] = allIndividuals.filter { - case (_, individual) => individual.getRdfType.toString == OntologyConstants.SalsahGui.GuiElementClass - } - - guiElementIndividuals.map { - case (guiElementIri, guiElementIndividual) => - val attributeDefs: Set[SalsahGuiAttributeDefinition] = - guiElementIndividual.predicates.get(OntologyConstants.SalsahGui.GuiAttributeDefinition.toSmartIri) match { - case Some(predicateInfo) => - predicateInfo.objects.map { - case StringLiteralV2(attributeDefStr, None) => - stringFormatter.toSalsahGuiAttributeDefinition( - attributeDefStr, - throw InconsistentRepositoryDataException( - s"Invalid salsah-gui:guiAttributeDefinition in $guiElementIri: $attributeDefStr") - ) - - case other => - throw InconsistentRepositoryDataException( - s"Invalid salsah-gui:guiAttributeDefinition in $guiElementIri: $other") - }.toSet - - case None => Set.empty[SalsahGuiAttributeDefinition] - } - - guiElementIri -> attributeDefs - } - } - - /** - * Validates the GUI attributes of a resource class property. - * - * @param propertyInfoContent the property definition. - * @param allGuiAttributeDefinitions the GUI attribute definitions for each GUI element. - * @param errorFun a function that throws an exception. It will be passed the message to be included in the exception. - */ - private def validateGuiAttributes(propertyInfoContent: PropertyInfoContentV2, - allGuiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]], - errorFun: String => Nothing): Unit = { - val propertyIri = propertyInfoContent.propertyIri - val predicates = propertyInfoContent.predicates - - // Find out which salsah-gui:Guielement the property uses, if any. - val maybeGuiElementPred: Option[PredicateInfoV2] = - predicates.get(OntologyConstants.SalsahGui.GuiElementProp.toSmartIri) - val maybeGuiElementIri: Option[SmartIri] = maybeGuiElementPred.map( - _.requireIriObject(throw InconsistentRepositoryDataException( - s"Property $propertyIri has an invalid object for ${OntologyConstants.SalsahGui.GuiElementProp}"))) - - // Get that Guielement's attribute definitions, if any. - val guiAttributeDefs: Set[SalsahGuiAttributeDefinition] = maybeGuiElementIri match { - case Some(guiElementIri) => - allGuiAttributeDefinitions.getOrElse( - guiElementIri, - errorFun(s"Property $propertyIri has salsah-gui:guiElement $guiElementIri, which doesn't exist")) - - case None => Set.empty[SalsahGuiAttributeDefinition] - } - - // If the property has the predicate salsah-gui:guiAttribute, syntactically validate the objects of that predicate. - val guiAttributes: Set[SalsahGuiAttribute] = - predicates.get(OntologyConstants.SalsahGui.GuiAttribute.toSmartIri) match { - case Some(guiAttributePred) => - val guiElementIri = maybeGuiElementIri.getOrElse( - errorFun(s"Property $propertyIri has salsah-gui:guiAttribute, but no salsah-gui:guiElement")) - - if (guiAttributeDefs.isEmpty) { - errorFun( - s"Property $propertyIri has salsah-gui:guiAttribute, but $guiElementIri has no salsah-gui:guiAttributeDefinition") - } - - // Syntactically validate each attribute. - guiAttributePred.objects.map { - case StringLiteralV2(guiAttributeObj, None) => - stringFormatter.toSalsahGuiAttribute( - s = guiAttributeObj, - attributeDefs = guiAttributeDefs, - errorFun = - errorFun(s"Property $propertyIri contains an invalid salsah-gui:guiAttribute: $guiAttributeObj") - ) - - case other => - errorFun(s"Property $propertyIri contains an invalid salsah-gui:guiAttribute: $other") - }.toSet - - case None => Set.empty[SalsahGuiAttribute] - } - - // Check that all required GUI attributes are provided. - val requiredAttributeNames = guiAttributeDefs.filter(_.isRequired).map(_.attributeName) - val providedAttributeNames = guiAttributes.map(_.attributeName) - val missingAttributeNames: Set[String] = requiredAttributeNames -- providedAttributeNames - - if (missingAttributeNames.nonEmpty) { - errorFun( - s"Property $propertyIri has one or more missing objects of salsah-gui:guiAttribute: ${missingAttributeNames - .mkString(", ")}") - } - } - - /** - * Updates the ontology cache. - * - * @param cacheData the updated data to be cached. - */ - private def storeCacheData(cacheData: OntologyCacheData): Unit = { - CacheUtil.put(cacheName = OntologyCacheName, key = OntologyCacheKey, value = cacheData) - } - - /** - * Gets the ontology data from the cache. - * - * @return an [[OntologyCacheData]] - */ - private def getCacheData: Future[OntologyCacheData] = { - Future { - CacheUtil.get[OntologyCacheData](cacheName = OntologyCacheName, key = OntologyCacheKey) match { - case Some(data) => data - case None => - throw ApplicationCacheException( - s"The Knora API server has not loaded any ontologies, perhaps because of an invalid ontology") - } - } - } - - /** - * Given a list of resource IRIs and a list of property IRIs (ontology entities), returns an [[EntityInfoGetResponseV2]] describing both resource and property entities. - * - * @param classIris the IRIs of the resource entities to be queried. - * @param propertyIris the IRIs of the property entities to be queried. - * @param requestingUser the user making the request. - * @return an [[EntityInfoGetResponseV2]]. - */ - private def getEntityInfoResponseV2(classIris: Set[SmartIri] = Set.empty[SmartIri], - propertyIris: Set[SmartIri] = Set.empty[SmartIri], - requestingUser: UserADM): Future[EntityInfoGetResponseV2] = { - for { - cacheData <- getCacheData - - // See if any of the requested entities are not Knora entities. - - nonKnoraEntities = (classIris ++ propertyIris).filter(!_.isKnoraEntityIri) - - _ = if (nonKnoraEntities.nonEmpty) { - throw BadRequestException(s"Some requested entities are not Knora entities: ${nonKnoraEntities.mkString(", ")}") - } - - // See if any of the requested entities are unavailable in the requested schema. - - classesUnavailableInSchema: Set[SmartIri] = classIris.foldLeft(Set.empty[SmartIri]) { - case (acc, classIri) => - // Is this class IRI hard-coded in the requested schema? - if (KnoraBaseToApiV2SimpleTransformationRules.externalClassesToAdd.contains(classIri) || - KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd.contains(classIri)) { - // Yes, so it's available. - acc - } else { - // No. Is it among the classes removed from the internal ontology in the requested schema? - classIri.getOntologySchema.get match { - case apiV2Schema: ApiV2Schema => - val internalClassIri = classIri.toOntologySchema(InternalSchema) - val knoraBaseClassesToRemove = OntologyTransformationRules - .getTransformationRules(classIri.getOntologyFromEntity, apiV2Schema) - .internalClassesToRemove - - if (knoraBaseClassesToRemove.contains(internalClassIri)) { - // Yes. Include it in the set of unavailable classes. - acc + classIri - } else { - // No. It's available. - acc - } - - case InternalSchema => acc - } - } - } - - propertiesUnavailableInSchema: Set[SmartIri] = propertyIris.foldLeft(Set.empty[SmartIri]) { - case (acc, propertyIri) => - // Is this property IRI hard-coded in the requested schema? - if (KnoraBaseToApiV2SimpleTransformationRules.externalPropertiesToAdd.contains(propertyIri) || - KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd.contains(propertyIri)) { - // Yes, so it's available. - acc - } else { - // No. See if it's available in the requested schema. - propertyIri.getOntologySchema.get match { - case apiV2Schema: ApiV2Schema => - val internalPropertyIri = propertyIri.toOntologySchema(InternalSchema) - - // If it's a link value property and it's requested in the simple schema, it's unavailable. - if (apiV2Schema == ApiV2Simple && isLinkValueProp(internalPropertyIri, cacheData)) { - acc + propertyIri - } else { - // Is it among the properties removed from the internal ontology in the requested schema? - - val knoraBasePropertiesToRemove = OntologyTransformationRules - .getTransformationRules(propertyIri.getOntologyFromEntity, apiV2Schema) - .internalPropertiesToRemove - - if (knoraBasePropertiesToRemove.contains(internalPropertyIri)) { - // Yes. Include it in the set of unavailable properties. - acc + propertyIri - } else { - // No. It's available. - acc - } - } - - case InternalSchema => acc - } - } - } - - entitiesUnavailableInSchema = classesUnavailableInSchema ++ propertiesUnavailableInSchema - - _ = if (entitiesUnavailableInSchema.nonEmpty) { - throw NotFoundException( - s"Some requested entities were not found: ${entitiesUnavailableInSchema.mkString(", ")}") - } - - // See if any of the requested entities are hard-coded for knora-api. - - hardCodedExternalClassesAvailable: Map[SmartIri, ReadClassInfoV2] = KnoraBaseToApiV2SimpleTransformationRules.externalClassesToAdd.view - .filterKeys(classIris) - .toMap ++ - KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd.view.filterKeys(classIris).toMap - - hardCodedExternalPropertiesAvailable: Map[SmartIri, ReadPropertyInfoV2] = KnoraBaseToApiV2SimpleTransformationRules.externalPropertiesToAdd.view - .filterKeys(propertyIris) - .toMap ++ - KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd.filterKeys(propertyIris) - - // Convert the remaining external entity IRIs to internal ones. - - internalToExternalClassIris: Map[SmartIri, SmartIri] = (classIris -- hardCodedExternalClassesAvailable.keySet) - .map(externalIri => externalIri.toOntologySchema(InternalSchema) -> externalIri) - .toMap - internalToExternalPropertyIris: Map[SmartIri, SmartIri] = (propertyIris -- hardCodedExternalPropertiesAvailable.keySet) - .map(externalIri => externalIri.toOntologySchema(InternalSchema) -> externalIri) - .toMap - - classIrisForCache = internalToExternalClassIris.keySet - propertyIrisForCache = internalToExternalPropertyIris.keySet - - // Get the entities that are available in the ontology cache. - - classOntologiesForCache: Iterable[ReadOntologyV2] = cacheData.ontologies.view - .filterKeys(classIrisForCache.map(_.getOntologyFromEntity)) - .toMap - .values - propertyOntologiesForCache: Iterable[ReadOntologyV2] = cacheData.ontologies.view - .filterKeys(propertyIrisForCache.map(_.getOntologyFromEntity)) - .toMap - .values - - classesAvailableFromCache: Map[SmartIri, ReadClassInfoV2] = classOntologiesForCache.flatMap { ontology => - ontology.classes.view.filterKeys(classIrisForCache).toMap - }.toMap - - propertiesAvailableFromCache: Map[SmartIri, ReadPropertyInfoV2] = propertyOntologiesForCache.flatMap { ontology => - ontology.properties.view.filterKeys(propertyIrisForCache).toMap - }.toMap - - allClassesAvailable: Map[SmartIri, ReadClassInfoV2] = classesAvailableFromCache ++ hardCodedExternalClassesAvailable - allPropertiesAvailable: Map[SmartIri, ReadPropertyInfoV2] = propertiesAvailableFromCache ++ hardCodedExternalPropertiesAvailable - - // See if any entities are missing. - - allExternalClassIrisAvailable: Set[SmartIri] = allClassesAvailable.keySet.map { classIri => - if (classIri.getOntologySchema.contains(InternalSchema)) { - internalToExternalClassIris(classIri) - } else { - classIri - } - } - - allExternalPropertyIrisAvailable = allPropertiesAvailable.keySet.map { propertyIri => - if (propertyIri.getOntologySchema.contains(InternalSchema)) { - internalToExternalPropertyIris(propertyIri) - } else { - propertyIri - } - } - - missingClasses = classIris -- allExternalClassIrisAvailable - missingProperties = propertyIris -- allExternalPropertyIrisAvailable - - missingEntities = missingClasses ++ missingProperties - - _ = if (missingEntities.nonEmpty) { - throw NotFoundException(s"Some requested entities were not found: ${missingEntities.mkString(", ")}") - } - - response = EntityInfoGetResponseV2( - classInfoMap = new ErrorHandlingMap(allClassesAvailable, { key => - s"Resource class $key not found" - }), - propertyInfoMap = new ErrorHandlingMap(allPropertiesAvailable, { key => - s"Property $key not found" - }) - ) - } yield response - } - - /** - * Given a list of standoff class IRIs and a list of property IRIs (ontology entities), returns an [[StandoffEntityInfoGetResponseV2]] describing both resource and property entities. - * - * @param standoffClassIris the IRIs of the resource entities to be queried. - * @param standoffPropertyIris the IRIs of the property entities to be queried. - * @param requestingUser the user making the request. - * @return a [[StandoffEntityInfoGetResponseV2]]. - */ - private def getStandoffEntityInfoResponseV2(standoffClassIris: Set[SmartIri] = Set.empty[SmartIri], - standoffPropertyIris: Set[SmartIri] = Set.empty[SmartIri], - requestingUser: UserADM): Future[StandoffEntityInfoGetResponseV2] = { + * Given a list of resource IRIs and a list of property IRIs (ontology entities), returns an [[EntityInfoGetResponseV2]] describing both resource and property entities. + * + * @param classIris the IRIs of the resource entities to be queried. + * @param propertyIris the IRIs of the property entities to be queried. + * @param requestingUser the user making the request. + * @return an [[EntityInfoGetResponseV2]]. + */ + private def getEntityInfoResponseV2( + classIris: Set[SmartIri] = Set.empty[SmartIri], + propertyIris: Set[SmartIri] = Set.empty[SmartIri], + requestingUser: UserADM + ): Future[EntityInfoGetResponseV2] = + OntologyHelpers.getEntityInfoResponseV2(classIris, propertyIris, requestingUser) + + /** + * Given a list of standoff class IRIs and a list of property IRIs (ontology entities), returns an [[StandoffEntityInfoGetResponseV2]] describing both resource and property entities. + * + * @param standoffClassIris the IRIs of the resource entities to be queried. + * @param standoffPropertyIris the IRIs of the property entities to be queried. + * @param requestingUser the user making the request. + * @return a [[StandoffEntityInfoGetResponseV2]]. + */ + private def getStandoffEntityInfoResponseV2( + standoffClassIris: Set[SmartIri] = Set.empty[SmartIri], + standoffPropertyIris: Set[SmartIri] = Set.empty[SmartIri], + requestingUser: UserADM + ): Future[StandoffEntityInfoGetResponseV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData - entitiesInWrongSchema = (standoffClassIris ++ standoffPropertyIris).filter( - _.getOntologySchema.contains(ApiV2Simple)) + entitiesInWrongSchema = + (standoffClassIris ++ standoffPropertyIris).filter(_.getOntologySchema.contains(ApiV2Simple)) _ = if (entitiesInWrongSchema.nonEmpty) { - throw NotFoundException( - s"Some requested standoff classes were not found: ${entitiesInWrongSchema.mkString(", ")}") - } + throw NotFoundException( + s"Some requested standoff classes were not found: ${entitiesInWrongSchema.mkString(", ")}" + ) + } - classIrisForCache = standoffClassIris.map(_.toOntologySchema(InternalSchema)) + classIrisForCache = standoffClassIris.map(_.toOntologySchema(InternalSchema)) propertyIrisForCache = standoffPropertyIris.map(_.toOntologySchema(InternalSchema)) - classOntologies: Iterable[ReadOntologyV2] = cacheData.ontologies - .filterKeys(classIrisForCache.map(_.getOntologyFromEntity)) - .values - propertyOntologies: Iterable[ReadOntologyV2] = cacheData.ontologies - .filterKeys(propertyIrisForCache.map(_.getOntologyFromEntity)) - .values + classOntologies: Iterable[ReadOntologyV2] = cacheData.ontologies.view + .filterKeys(classIrisForCache.map(_.getOntologyFromEntity)) + .values + propertyOntologies: Iterable[ReadOntologyV2] = cacheData.ontologies.view + .filterKeys(propertyIrisForCache.map(_.getOntologyFromEntity)) + .values classDefsAvailable: Map[SmartIri, ReadClassInfoV2] = classOntologies.flatMap { ontology => - ontology.classes.filter { - case (classIri, classDef) => classDef.isStandoffClass && standoffClassIris.contains(classIri) - } - }.toMap + ontology.classes.filter { case (classIri, classDef) => + classDef.isStandoffClass && standoffClassIris.contains( + classIri + ) + } + }.toMap propertyDefsAvailable: Map[SmartIri, ReadPropertyInfoV2] = propertyOntologies.flatMap { ontology => - ontology.properties.filter { - case (propertyIri, _) => - standoffPropertyIris.contains(propertyIri) && cacheData.standoffProperties.contains(propertyIri) - } - }.toMap - - missingClassDefs = classIrisForCache -- classDefsAvailable.keySet + ontology.properties.filter { case (propertyIri, _) => + standoffPropertyIris.contains( + propertyIri + ) && cacheData.standoffProperties.contains( + propertyIri + ) + } + }.toMap + + missingClassDefs = classIrisForCache -- classDefsAvailable.keySet missingPropertyDefs = propertyIrisForCache -- propertyDefsAvailable.keySet _ = if (missingClassDefs.nonEmpty) { - throw NotFoundException(s"Some requested standoff classes were not found: ${missingClassDefs.mkString(", ")}") - } + throw NotFoundException( + s"Some requested standoff classes were not found: ${missingClassDefs.mkString(", ")}" + ) + } _ = if (missingPropertyDefs.nonEmpty) { - throw NotFoundException( - s"Some requested standoff properties were not found: ${missingPropertyDefs.mkString(", ")}") - } + throw NotFoundException( + s"Some requested standoff properties were not found: ${missingPropertyDefs.mkString(", ")}" + ) + } response = StandoffEntityInfoGetResponseV2( - standoffClassInfoMap = new ErrorHandlingMap(classDefsAvailable, { key => - s"Resource class $key not found" - }), - standoffPropertyInfoMap = new ErrorHandlingMap(propertyDefsAvailable, { key => - s"Property $key not found" - }) - ) + standoffClassInfoMap = + new ErrorHandlingMap(classDefsAvailable, key => s"Resource class $key not found"), + standoffPropertyInfoMap = + new ErrorHandlingMap(propertyDefsAvailable, key => s"Property $key not found") + ) } yield response - } /** - * Gets information about all standoff classes that are a subclass of a data type standoff class. - * - * @param requestingUser the user making the request. - * @return a [[StandoffClassesWithDataTypeGetResponseV2]] - */ + * Gets information about all standoff classes that are a subclass of a data type standoff class. + * + * @param requestingUser the user making the request. + * @return a [[StandoffClassesWithDataTypeGetResponseV2]] + */ private def getStandoffStandoffClassesWithDataTypeV2( - requestingUser: UserADM): Future[StandoffClassesWithDataTypeGetResponseV2] = { + requestingUser: UserADM + ): Future[StandoffClassesWithDataTypeGetResponseV2] = for { - cacheData <- getCacheData - } yield - StandoffClassesWithDataTypeGetResponseV2( - standoffClassInfoMap = cacheData.ontologies.values.flatMap { ontology => - ontology.classes.filter { - case (_, classDef) => classDef.isStandoffClass && classDef.standoffDataType.isDefined - } - }.toMap - ) - } + cacheData <- Cache.getCacheData + } yield StandoffClassesWithDataTypeGetResponseV2( + standoffClassInfoMap = cacheData.ontologies.values.flatMap { ontology => + ontology.classes.filter { case (_, classDef) => + classDef.isStandoffClass && classDef.standoffDataType.isDefined + } + }.toMap + ) /** - * Gets all standoff property entities. - * - * @param requestingUser the user making the request. - * @return a [[StandoffAllPropertyEntitiesGetResponseV2]]. - */ + * Gets all standoff property entities. + * + * @param requestingUser the user making the request. + * @return a [[StandoffAllPropertyEntitiesGetResponseV2]]. + */ private def getAllStandoffPropertyEntitiesV2( - requestingUser: UserADM): Future[StandoffAllPropertyEntitiesGetResponseV2] = { + requestingUser: UserADM + ): Future[StandoffAllPropertyEntitiesGetResponseV2] = for { - cacheData <- getCacheData - } yield - StandoffAllPropertyEntitiesGetResponseV2( - standoffAllPropertiesEntityInfoMap = cacheData.ontologies.values.flatMap { ontology => - ontology.properties.filterKeys(cacheData.standoffProperties) - }.toMap - ) - } + cacheData <- Cache.getCacheData + } yield StandoffAllPropertyEntitiesGetResponseV2( + standoffAllPropertiesEntityInfoMap = cacheData.ontologies.values.flatMap { ontology => + ontology.properties.view.filterKeys(cacheData.standoffProperties) + }.toMap + ) /** - * Checks whether a certain Knora resource or value class is a subclass of another class. - * - * @param subClassIri the IRI of the resource or value class whose subclassOf relations have to be checked. - * @param superClassIri the IRI of the resource or value class to check for (whether it is a a super class of `subClassIri` or not). - * @return a [[CheckSubClassResponseV2]]. - */ - private def checkSubClassV2(subClassIri: SmartIri, superClassIri: SmartIri): Future[CheckSubClassResponseV2] = { + * Checks whether a certain Knora resource or value class is a subclass of another class. + * + * @param subClassIri the IRI of the resource or value class whose subclassOf relations have to be checked. + * @param superClassIri the IRI of the resource or value class to check for (whether it is a a super class of `subClassIri` or not). + * @return a [[CheckSubClassResponseV2]]. + */ + private def checkSubClassV2(subClassIri: SmartIri, superClassIri: SmartIri): Future[CheckSubClassResponseV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData response = CheckSubClassResponseV2( - isSubClass = cacheData.subClassOfRelations.get(subClassIri) match { - case Some(baseClasses) => baseClasses.contains(superClassIri) - case None => throw BadRequestException(s"Class $subClassIri not found") - } - ) + isSubClass = cacheData.subClassOfRelations.get(subClassIri) match { + case Some(baseClasses) => baseClasses.contains(superClassIri) + case None => throw BadRequestException(s"Class $subClassIri not found") + } + ) } yield response - } /** - * Gets the IRIs of the subclasses of a class. - * - * @param classIri the IRI of the class whose subclasses should be returned. - * @return a [[SubClassesGetResponseV2]]. - */ - private def getSubClassesV2(classIri: SmartIri, requestingUser: UserADM): Future[SubClassesGetResponseV2] = { + * Gets the IRIs of the subclasses of a class. + * + * @param classIri the IRI of the class whose subclasses should be returned. + * @return a [[SubClassesGetResponseV2]]. + */ + private def getSubClassesV2(classIri: SmartIri, requestingUser: UserADM): Future[SubClassesGetResponseV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData subClassIris = cacheData.superClassOfRelations(classIri).toVector.sorted subClasses = subClassIris.map { subClassIri => - val classInfo: ReadClassInfoV2 = cacheData.ontologies(subClassIri.getOntologyFromEntity).classes(subClassIri) - - SubClassInfoV2( - id = subClassIri, - label = classInfo.entityInfoContent - .getPredicateStringLiteralObject( - predicateIri = OntologyConstants.Rdfs.Label.toSmartIri, - preferredLangs = Some(requestingUser.lang, settings.fallbackLanguage) - ) - .getOrElse(throw InconsistentRepositoryDataException(s"Resource class $subClassIri has no rdfs:label")) - ) - } - } yield - SubClassesGetResponseV2( - subClasses = subClasses - ) - } + val classInfo: ReadClassInfoV2 = + cacheData.ontologies(subClassIri.getOntologyFromEntity).classes(subClassIri) + + SubClassInfoV2( + id = subClassIri, + label = classInfo.entityInfoContent + .getPredicateStringLiteralObject( + predicateIri = OntologyConstants.Rdfs.Label.toSmartIri, + preferredLangs = Some(requestingUser.lang, settings.fallbackLanguage) + ) + .getOrElse( + throw InconsistentRepositoryDataException(s"Resource class $subClassIri has no rdfs:label") + ) + ) + } + } yield SubClassesGetResponseV2( + subClasses = subClasses + ) /** - * Gets the [[OntologyKnoraEntitiesIriInfoV2]] for an ontology. - * - * @param ontologyIri the IRI of the ontology to query - * @param requestingUser the user making the request. - * @return an [[OntologyKnoraEntitiesIriInfoV2]]. - */ - private def getKnoraEntityIrisInNamedGraphV2(ontologyIri: SmartIri, - requestingUser: UserADM): Future[OntologyKnoraEntitiesIriInfoV2] = { + * Gets the [[OntologyKnoraEntitiesIriInfoV2]] for an ontology. + * + * @param ontologyIri the IRI of the ontology to query + * @param requestingUser the user making the request. + * @return an [[OntologyKnoraEntitiesIriInfoV2]]. + */ + private def getKnoraEntityIrisInNamedGraphV2( + ontologyIri: SmartIri, + requestingUser: UserADM + ): Future[OntologyKnoraEntitiesIriInfoV2] = for { - cacheData <- getCacheData - ontology = cacheData.ontologies(ontologyIri) - } yield - OntologyKnoraEntitiesIriInfoV2( - ontologyIri = ontologyIri, - propertyIris = ontology.properties.keySet.filter { propertyIri => - isKnoraResourceProperty(propertyIri, cacheData) - }, - classIris = ontology.classes.filter { - case (_, classDef) => classDef.isResourceClass - }.keySet, - standoffClassIris = ontology.classes.filter { - case (_, classDef) => classDef.isStandoffClass - }.keySet, - standoffPropertyIris = ontology.properties.keySet.filter(cacheData.standoffProperties) - ) - } + cacheData <- Cache.getCacheData + ontology = cacheData.ontologies(ontologyIri) + } yield OntologyKnoraEntitiesIriInfoV2( + ontologyIri = ontologyIri, + propertyIris = ontology.properties.keySet.filter { propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + }, + classIris = ontology.classes.filter { case (_, classDef) => + classDef.isResourceClass + }.keySet, + standoffClassIris = ontology.classes.filter { case (_, classDef) => + classDef.isStandoffClass + }.keySet, + standoffPropertyIris = ontology.properties.keySet.filter(cacheData.standoffProperties) + ) /** - * Gets the metadata describing the ontologies that belong to selected projects, or to all projects. - * - * @param projectIris the IRIs of the projects selected, or an empty set if all projects are selected. - * @param requestingUser the user making the request. - * @return a [[ReadOntologyMetadataV2]]. - */ - private def getOntologyMetadataForProjectsV2(projectIris: Set[SmartIri], - requestingUser: UserADM): Future[ReadOntologyMetadataV2] = { + * Gets the metadata describing the ontologies that belong to selected projects, or to all projects. + * + * @param projectIris the IRIs of the projects selected, or an empty set if all projects are selected. + * @param requestingUser the user making the request. + * @return a [[ReadOntologyMetadataV2]]. + */ + private def getOntologyMetadataForProjectsV2( + projectIris: Set[SmartIri], + requestingUser: UserADM + ): Future[ReadOntologyMetadataV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData returnAllOntologies: Boolean = projectIris.isEmpty ontologyMetadata: Set[OntologyMetadataV2] = if (returnAllOntologies) { - cacheData.ontologies.values.map(_.ontologyMetadata).toSet - } else { - cacheData.ontologies.values - .filter { ontology => - projectIris.contains(ontology.ontologyMetadata.projectIri.get) - } - .map { ontology => - ontology.ontologyMetadata - } - .toSet - } - } yield - ReadOntologyMetadataV2( - ontologies = ontologyMetadata - ) - } + cacheData.ontologies.values.map(_.ontologyMetadata).toSet + } else { + cacheData.ontologies.values.filter { ontology => + projectIris.contains(ontology.ontologyMetadata.projectIri.get) + }.map { ontology => + ontology.ontologyMetadata + }.toSet + } + } yield ReadOntologyMetadataV2( + ontologies = ontologyMetadata + ) /** - * Gets the metadata describing the specified ontologies, or all ontologies. - * - * @param ontologyIris the IRIs of the ontologies selected, or an empty set if all ontologies are selected. - * @param requestingUser the user making the request. - * @return a [[ReadOntologyMetadataV2]]. - */ - private def getOntologyMetadataByIriV2(ontologyIris: Set[SmartIri], - requestingUser: UserADM): Future[ReadOntologyMetadataV2] = { + * Gets the metadata describing the specified ontologies, or all ontologies. + * + * @param ontologyIris the IRIs of the ontologies selected, or an empty set if all ontologies are selected. + * @param requestingUser the user making the request. + * @return a [[ReadOntologyMetadataV2]]. + */ + private def getOntologyMetadataByIriV2( + ontologyIris: Set[SmartIri], + requestingUser: UserADM + ): Future[ReadOntologyMetadataV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData returnAllOntologies: Boolean = ontologyIris.isEmpty ontologyMetadata: Set[OntologyMetadataV2] = if (returnAllOntologies) { - cacheData.ontologies.values.map(_.ontologyMetadata).toSet - } else { - val ontologyIrisForCache = ontologyIris.map(_.toOntologySchema(InternalSchema)) - val missingOntologies = ontologyIrisForCache -- cacheData.ontologies.keySet - - if (missingOntologies.nonEmpty) { - throw BadRequestException( - s"One or more requested ontologies were not found: ${missingOntologies.mkString(", ")}") - } - - cacheData.ontologies - .filterKeys(ontologyIrisForCache) - .values - .map { ontology => - ontology.ontologyMetadata - } - .toSet - } - } yield - ReadOntologyMetadataV2( - ontologies = ontologyMetadata - ) - } + cacheData.ontologies.values.map(_.ontologyMetadata).toSet + } else { + val ontologyIrisForCache = + ontologyIris.map(_.toOntologySchema(InternalSchema)) + val missingOntologies = + ontologyIrisForCache -- cacheData.ontologies.keySet + + if (missingOntologies.nonEmpty) { + throw BadRequestException( + s"One or more requested ontologies were not found: ${missingOntologies + .mkString(", ")}" + ) + } + + cacheData.ontologies.view + .filterKeys(ontologyIrisForCache) + .values + .map { ontology => + ontology.ontologyMetadata + } + .toSet + } + } yield ReadOntologyMetadataV2( + ontologies = ontologyMetadata + ) /** - * Requests the entities defined in the given ontology. - * - * @param ontologyIri the IRI (internal or external) of the ontology to be queried. - * @param requestingUser the user making the request. - * @return a [[ReadOntologyV2]]. - */ - private def getOntologyEntitiesV2(ontologyIri: SmartIri, - allLanguages: Boolean, - requestingUser: UserADM): Future[ReadOntologyV2] = { + * Requests the entities defined in the given ontology. + * + * @param ontologyIri the IRI (internal or external) of the ontology to be queried. + * @param requestingUser the user making the request. + * @return a [[ReadOntologyV2]]. + */ + private def getOntologyEntitiesV2( + ontologyIri: SmartIri, + allLanguages: Boolean, + requestingUser: UserADM + ): Future[ReadOntologyV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData _ = if (ontologyIri.getOntologyName == "standoff" && ontologyIri.getOntologySchema.contains(ApiV2Simple)) { - throw BadRequestException(s"The standoff ontology is not available in the API v2 simple schema") - } + throw BadRequestException(s"The standoff ontology is not available in the API v2 simple schema") + } ontology = cacheData.ontologies.get(ontologyIri.toOntologySchema(InternalSchema)) match { - case Some(cachedOntology) => cachedOntology - case None => throw NotFoundException(s"Ontology not found: $ontologyIri") - } + case Some(cachedOntology) => cachedOntology + case None => throw NotFoundException(s"Ontology not found: $ontologyIri") + } // Are we returning data in the user's preferred language, or in all available languages? userLang = if (!allLanguages) { - // Just the user's preferred language. - Some(requestingUser.lang) - } else { - // All available languages. - None - } - } yield - ontology.copy( - userLang = userLang - ) - } - - /** - * Requests information about OWL classes in a single ontology. - * - * @param classIris the IRIs (internal or external) of the classes to query for. - * @param requestingUser the user making the request. - * @return a [[ReadOntologyV2]]. - */ - private def getClassDefinitionsFromOntologyV2(classIris: Set[SmartIri], - allLanguages: Boolean, - requestingUser: UserADM): Future[ReadOntologyV2] = { - for { - cacheData <- getCacheData - - ontologyIris = classIris.map(_.getOntologyFromEntity) - - _ = if (ontologyIris.size != 1) { - throw BadRequestException(s"Only one ontology may be queried per request") - } - - classInfoResponse: EntityInfoGetResponseV2 <- getEntityInfoResponseV2(classIris = classIris, - requestingUser = requestingUser) - internalOntologyIri = ontologyIris.head.toOntologySchema(InternalSchema) - - // Are we returning data in the user's preferred language, or in all available languages? - userLang = if (!allLanguages) { - // Just the user's preferred language. - Some(requestingUser.lang) - } else { - // All available languages. - None - } - } yield - ReadOntologyV2( - ontologyMetadata = cacheData.ontologies(internalOntologyIri).ontologyMetadata, - classes = classInfoResponse.classInfoMap, - userLang = userLang - ) - } + // Just the user's preferred language. + Some(requestingUser.lang) + } else { + // All available languages. + None + } + } yield ontology.copy( + userLang = userLang + ) /** - * Requests information about properties in a single ontology. - * - * @param propertyIris the IRIs (internal or external) of the properties to query for. - * @param requestingUser the user making the request. - * @return a [[ReadOntologyV2]]. - */ - private def getPropertyDefinitionsFromOntologyV2(propertyIris: Set[SmartIri], - allLanguages: Boolean, - requestingUser: UserADM): Future[ReadOntologyV2] = { + * Requests information about OWL classes in a single ontology. + * + * @param classIris the IRIs (internal or external) of the classes to query for. + * @param requestingUser the user making the request. + * @return a [[ReadOntologyV2]]. + */ + private def getClassDefinitionsFromOntologyV2( + classIris: Set[SmartIri], + allLanguages: Boolean, + requestingUser: UserADM + ): Future[ReadOntologyV2] = + OntologyHelpers.getClassDefinitionsFromOntologyV2(classIris, allLanguages, requestingUser) + + /** + * Requests information about properties in a single ontology. + * + * @param propertyIris the IRIs (internal or external) of the properties to query for. + * @param requestingUser the user making the request. + * @return a [[ReadOntologyV2]]. + */ + private def getPropertyDefinitionsFromOntologyV2( + propertyIris: Set[SmartIri], + allLanguages: Boolean, + requestingUser: UserADM + ): Future[ReadOntologyV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData ontologyIris = propertyIris.map(_.getOntologyFromEntity) _ = if (ontologyIris.size != 1) { - throw BadRequestException(s"Only one ontology may be queried per request") - } + throw BadRequestException(s"Only one ontology may be queried per request") + } - propertyInfoResponse: EntityInfoGetResponseV2 <- getEntityInfoResponseV2(propertyIris = propertyIris, - requestingUser = requestingUser) + propertyInfoResponse: EntityInfoGetResponseV2 <- + getEntityInfoResponseV2(propertyIris = propertyIris, requestingUser = requestingUser) internalOntologyIri = ontologyIris.head.toOntologySchema(InternalSchema) // Are we returning data in the user's preferred language, or in all available languages? userLang = if (!allLanguages) { - // Just the user's preferred language. - Some(requestingUser.lang) - } else { - // All available languages. - None - } - } yield - ReadOntologyV2( - ontologyMetadata = cacheData.ontologies(internalOntologyIri).ontologyMetadata, - properties = propertyInfoResponse.propertyInfoMap, - userLang = userLang - ) - } - - /** - * Reads an ontology's metadata. - * - * @param internalOntologyIri the ontology's internal IRI. - * @param featureFactoryConfig the feature factory configuration. - * @return an [[OntologyMetadataV2]], or [[None]] if the ontology is not found. - */ - private def loadOntologyMetadata(internalOntologyIri: SmartIri, - featureFactoryConfig: FeatureFactoryConfig): Future[Option[OntologyMetadataV2]] = { - for { - _ <- Future { - if (!internalOntologyIri.getOntologySchema.contains(InternalSchema)) { - throw AssertionException(s"Expected an internal ontology IRI: $internalOntologyIri") - } - } - - getOntologyInfoSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getOntologyInfo( - triplestore = settings.triplestoreType, - ontologyIri = internalOntologyIri - ) - .toString() - - getOntologyInfoResponse <- (storeManager ? SparqlConstructRequest( - sparql = getOntologyInfoSparql, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlConstructResponse] - - metadata: Option[OntologyMetadataV2] = if (getOntologyInfoResponse.statements.isEmpty) { - None - } else { - getOntologyInfoResponse.statements.get(internalOntologyIri.toString) match { - case Some(statements: Seq[(IRI, String)]) => - val statementMap: Map[IRI, Seq[String]] = statements - .groupBy { - case (pred, _) => pred - } - .map { - case (pred, predStatements) => - pred -> predStatements.map { - case (_, obj) => obj - } - } - - val projectIris: Seq[String] = statementMap.getOrElse( - OntologyConstants.KnoraBase.AttachedToProject, - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has no knora-base:attachedToProject") - ) - val labels: Seq[String] = statementMap.getOrElse(OntologyConstants.Rdfs.Label, Seq.empty[String]) - val comments: Seq[String] = statementMap.getOrElse(OntologyConstants.Rdfs.Comment, Seq.empty[String]) - val lastModDates: Seq[String] = - statementMap.getOrElse(OntologyConstants.KnoraBase.LastModificationDate, Seq.empty[String]) - - val projectIri = if (projectIris.size > 1) { - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has more than one knora-base:attachedToProject") - } else { - projectIris.head.toSmartIri - } - - if (!internalOntologyIri.isKnoraBuiltInDefinitionIri) { - if (projectIri.toString == OntologyConstants.KnoraAdmin.SystemProject) { - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri cannot be in project ${OntologyConstants.KnoraAdmin.SystemProject}") - } - - if (internalOntologyIri.isKnoraSharedDefinitionIri && projectIri.toString != OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject) { - throw InconsistentRepositoryDataException( - s"Shared ontology $internalOntologyIri must be in project ${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}") - } - } - - val label: String = if (labels.size > 1) { - throw InconsistentRepositoryDataException(s"Ontology $internalOntologyIri has more than one rdfs:label") - } else if (labels.isEmpty) { - internalOntologyIri.getOntologyName - } else { - labels.head - } - - val comment: Option[String] = if (comments.size > 1) { - throw InconsistentRepositoryDataException(s"Ontology $internalOntologyIri has more than one rdfs:comment") - } else comments.headOption - - val lastModificationDate: Option[Instant] = if (lastModDates.size > 1) { - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has more than one ${OntologyConstants.KnoraBase.LastModificationDate}") - } else if (lastModDates.isEmpty) { - None - } else { - val dateStr = lastModDates.head - Some( - stringFormatter.xsdDateTimeStampToInstant( - dateStr, - throw InconsistentRepositoryDataException( - s"Invalid ${OntologyConstants.KnoraBase.LastModificationDate}: $dateStr"))) - } - - Some( - OntologyMetadataV2( - ontologyIri = internalOntologyIri, - projectIri = Some(projectIri), - label = Some(label), - comment = comment, - lastModificationDate = lastModificationDate - )) - - case None => None - } - } - } yield metadata - } + // Just the user's preferred language. + Some(requestingUser.lang) + } else { + // All available languages. + None + } + } yield ReadOntologyV2( + ontologyMetadata = cacheData.ontologies(internalOntologyIri).ontologyMetadata, + properties = propertyInfoResponse.propertyInfoMap, + userLang = userLang + ) /** - * Creates a new, empty ontology. - * - * @param createOntologyRequest the request message. - * @return a [[SuccessResponseV2]]. - */ + * Creates a new, empty ontology. + * + * @param createOntologyRequest the request message. + * @return a [[SuccessResponseV2]]. + */ private def createOntology(createOntologyRequest: CreateOntologyRequestV2): Future[ReadOntologyMetadataV2] = { - def makeTaskFuture(internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = { + def makeTaskFuture(internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData // Make sure the ontology doesn't already exist. - existingOntologyMetadata: Option[OntologyMetadataV2] <- loadOntologyMetadata( - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = createOntologyRequest.featureFactoryConfig - ) + existingOntologyMetadata: Option[OntologyMetadataV2] <- OntologyHelpers.loadOntologyMetadata( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = + createOntologyRequest.featureFactoryConfig + ) _ = if (existingOntologyMetadata.nonEmpty) { - throw BadRequestException( - s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} cannot be created, because it already exists") - } + throw BadRequestException( + s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} cannot be created, because it already exists" + ) + } // If this is a shared ontology, make sure it's in the default shared ontologies project. - _ = if (createOntologyRequest.isShared && createOntologyRequest.projectIri.toString != OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject) { - throw BadRequestException( - s"Shared ontologies must be created in project <${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}>") - } + _ = + if ( + createOntologyRequest.isShared && createOntologyRequest.projectIri.toString != OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject + ) { + throw BadRequestException( + s"Shared ontologies must be created in project <${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}>" + ) + } // If it's in the default shared ontologies project, make sure it's a shared ontology. - _ = if (createOntologyRequest.projectIri.toString == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject && !createOntologyRequest.isShared) { - throw BadRequestException( - s"Ontologies created in project <${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}> must be shared") - } + _ = + if ( + createOntologyRequest.projectIri.toString == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject && !createOntologyRequest.isShared + ) { + throw BadRequestException( + s"Ontologies created in project <${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}> must be shared" + ) + } // Create the ontology. currentTime: Instant = Instant.now createOntologySparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .createOntology( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - projectIri = createOntologyRequest.projectIri, - isShared = createOntologyRequest.isShared, - ontologyLabel = createOntologyRequest.label, - ontologyComment = createOntologyRequest.comment, - currentTime = currentTime - ) - .toString + .createOntology( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + projectIri = createOntologyRequest.projectIri, + isShared = createOntologyRequest.isShared, + ontologyLabel = createOntologyRequest.label, + ontologyComment = createOntologyRequest.comment, + currentTime = currentTime + ) + .toString _ <- (storeManager ? SparqlUpdateRequest(createOntologySparql)).mapTo[SparqlUpdateResponse] // Check that the update was successful. To do this, we have to undo the SPARQL-escaping of the input. unescapedNewMetadata = OntologyMetadataV2( - ontologyIri = internalOntologyIri, - projectIri = Some(createOntologyRequest.projectIri), - label = Some(createOntologyRequest.label), - comment = createOntologyRequest.comment, - lastModificationDate = Some(currentTime) - ).unescape - - maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- loadOntologyMetadata( - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = createOntologyRequest.featureFactoryConfig - ) + ontologyIri = internalOntologyIri, + projectIri = Some(createOntologyRequest.projectIri), + label = Some(createOntologyRequest.label), + comment = createOntologyRequest.comment, + lastModificationDate = Some(currentTime) + ).unescape + + maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- OntologyHelpers.loadOntologyMetadata( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = + createOntologyRequest.featureFactoryConfig + ) _ = maybeLoadedOntologyMetadata match { - case Some(loadedOntologyMetadata) => - if (loadedOntologyMetadata != unescapedNewMetadata) { - throw UpdateNotPerformedException() - } + case Some(loadedOntologyMetadata) => + if (loadedOntologyMetadata != unescapedNewMetadata) { + throw UpdateNotPerformedException() + } - case None => throw UpdateNotPerformedException() - } + case None => throw UpdateNotPerformedException() + } // Update the ontology cache with the unescaped metadata. - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> ReadOntologyV2( - ontologyMetadata = unescapedNewMetadata)) - )) + _ = + Cache.storeCacheData( + cacheData.copy( + ontologies = + cacheData.ontologies + (internalOntologyIri -> ReadOntologyV2(ontologyMetadata = unescapedNewMetadata)) + ) + ) } yield ReadOntologyMetadataV2(ontologies = Set(unescapedNewMetadata)) - } for { requestingUser <- FastFuture.successful(createOntologyRequest.requestingUser) - projectIri = createOntologyRequest.projectIri + projectIri = createOntologyRequest.projectIri // check if the requesting user is allowed to create an ontology - _ = if (!(requestingUser.permissions.isProjectAdmin(projectIri.toString) || requestingUser.permissions.isSystemAdmin)) { - // println(s"requestingUser: $requestingUser") - // println(s"requestingUser.permissionData.isProjectAdmin(<${projectIri.toString}>): ${requestingUser.permissionData.isProjectAdmin(projectIri.toString)}") - throw ForbiddenException( - s"A new ontology in the project ${createOntologyRequest.projectIri} can only be created by an admin of that project, or by a system admin.") - } + _ = + if ( + !(requestingUser.permissions.isProjectAdmin(projectIri.toString) || requestingUser.permissions.isSystemAdmin) + ) { + // println(s"requestingUser: $requestingUser") + // println(s"requestingUser.permissionData.isProjectAdmin(<${projectIri.toString}>): ${requestingUser.permissionData.isProjectAdmin(projectIri.toString)}") + throw ForbiddenException( + s"A new ontology in the project ${createOntologyRequest.projectIri} can only be created by an admin of that project, or by a system admin." + ) + } // Get project info for the shortcode. projectInfo: ProjectGetResponseADM <- (responderManager ? ProjectGetRequestADM( - identifier = ProjectIdentifierADM(maybeIri = Some(projectIri.toString)), - featureFactoryConfig = createOntologyRequest.featureFactoryConfig, - requestingUser = requestingUser - )).mapTo[ProjectGetResponseADM] + identifier = ProjectIdentifierADM(maybeIri = Some(projectIri.toString)), + featureFactoryConfig = createOntologyRequest.featureFactoryConfig, + requestingUser = requestingUser + )).mapTo[ProjectGetResponseADM] // Check that the ontology name is valid. - validOntologyName = stringFormatter.validateProjectSpecificOntologyName( - createOntologyRequest.ontologyName, - throw BadRequestException(s"Invalid project-specific ontology name: ${createOntologyRequest.ontologyName}")) + validOntologyName = + stringFormatter.validateProjectSpecificOntologyName( + createOntologyRequest.ontologyName, + throw BadRequestException(s"Invalid project-specific ontology name: ${createOntologyRequest.ontologyName}") + ) // Make the internal ontology IRI. - internalOntologyIri = stringFormatter.makeProjectSpecificInternalOntologyIri(validOntologyName, - createOntologyRequest.isShared, - projectInfo.project.shortcode) + internalOntologyIri = stringFormatter.makeProjectSpecificInternalOntologyIri( + validOntologyName, + createOntologyRequest.isShared, + projectInfo.project.shortcode + ) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = createOntologyRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => makeTaskFuture(internalOntologyIri) - ) + apiRequestID = createOntologyRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => makeTaskFuture(internalOntologyIri) + ) } yield taskResult } /** - * Changes ontology metadata. - * - * @param changeOntologyMetadataRequest the request to change the metadata. - * @return a [[ReadOntologyMetadataV2]] containing the new metadata. - */ + * Changes ontology metadata. + * + * @param changeOntologyMetadataRequest the request to change the metadata. + * @return a [[ReadOntologyMetadataV2]] containing the new metadata. + */ def changeOntologyMetadata( - changeOntologyMetadataRequest: ChangeOntologyMetadataRequestV2): Future[ReadOntologyMetadataV2] = { - def makeTaskFuture(internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = { + changeOntologyMetadataRequest: ChangeOntologyMetadataRequestV2 + ): Future[ReadOntologyMetadataV2] = { + def makeTaskFuture(internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData // Check that the user has permission to update the ontology. - projectIri <- checkPermissionsForOntologyUpdate( - internalOntologyIri = internalOntologyIri, - requestingUser = changeOntologyMetadataRequest.requestingUser - ) + projectIri <- OntologyHelpers.checkPermissionsForOntologyUpdate( + internalOntologyIri = internalOntologyIri, + requestingUser = changeOntologyMetadataRequest.requestingUser + ) // Check that the ontology exists and has not been updated by another user since the client last read its metadata. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changeOntologyMetadataRequest.lastModificationDate, - featureFactoryConfig = changeOntologyMetadataRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changeOntologyMetadataRequest.lastModificationDate, + featureFactoryConfig = changeOntologyMetadataRequest.featureFactoryConfig + ) // get the metadata of the ontology. oldMetadata: OntologyMetadataV2 = cacheData.ontologies(internalOntologyIri).ontologyMetadata @@ -2133,18 +684,18 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changeOntologyMetadata( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - newLabel = changeOntologyMetadataRequest.label, - hasOldComment = ontologyHasComment, - deleteOldComment = ontologyHasComment && changeOntologyMetadataRequest.comment.nonEmpty, - newComment = changeOntologyMetadataRequest.comment, - lastModificationDate = changeOntologyMetadataRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changeOntologyMetadata( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + newLabel = changeOntologyMetadataRequest.label, + hasOldComment = ontologyHasComment, + deleteOldComment = ontologyHasComment && changeOntologyMetadataRequest.comment.nonEmpty, + newComment = changeOntologyMetadataRequest.comment, + lastModificationDate = changeOntologyMetadataRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] @@ -2152,87 +703,93 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Is there any new label given? label = if (changeOntologyMetadataRequest.label.isEmpty) { - // No. Consider the old label for checking the update. - oldMetadata.label - } else { - // Yes. Consider the new label for checking the update. - changeOntologyMetadataRequest.label - } + // No. Consider the old label for checking the update. + oldMetadata.label + } else { + // Yes. Consider the new label for checking the update. + changeOntologyMetadataRequest.label + } // Is there any new comment given? comment = if (changeOntologyMetadataRequest.comment.isEmpty) { - // No. Consider the old comment for checking the update. - oldMetadata.comment - } else { - // Yes. Consider the new comment for checking the update. - changeOntologyMetadataRequest.comment - } + // No. Consider the old comment for checking the update. + oldMetadata.comment + } else { + // Yes. Consider the new comment for checking the update. + changeOntologyMetadataRequest.comment + } unescapedNewMetadata = OntologyMetadataV2( - ontologyIri = internalOntologyIri, - projectIri = Some(projectIri), - label = label, - comment = comment, - lastModificationDate = Some(currentTime) - ).unescape - - maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- loadOntologyMetadata( - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = changeOntologyMetadataRequest.featureFactoryConfig - ) + ontologyIri = internalOntologyIri, + projectIri = Some(projectIri), + label = label, + comment = comment, + lastModificationDate = Some(currentTime) + ).unescape + + maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- + OntologyHelpers.loadOntologyMetadata( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = changeOntologyMetadataRequest.featureFactoryConfig + ) _ = maybeLoadedOntologyMetadata match { - case Some(loadedOntologyMetadata) => - if (loadedOntologyMetadata != unescapedNewMetadata) { - throw UpdateNotPerformedException() - } + case Some(loadedOntologyMetadata) => + if (loadedOntologyMetadata != unescapedNewMetadata) { + throw UpdateNotPerformedException() + } - case None => throw UpdateNotPerformedException() - } + case None => throw UpdateNotPerformedException() + } // Update the ontology cache with the unescaped metadata. - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> cacheData - .ontologies(internalOntologyIri) - .copy(ontologyMetadata = unescapedNewMetadata)) - )) + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> cacheData + .ontologies(internalOntologyIri) + .copy(ontologyMetadata = unescapedNewMetadata)) + ) + ) } yield ReadOntologyMetadataV2(ontologies = Set(unescapedNewMetadata)) - } for { - _ <- checkExternalOntologyIriForUpdate(changeOntologyMetadataRequest.ontologyIri) + _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(changeOntologyMetadataRequest.ontologyIri) internalOntologyIri = changeOntologyMetadataRequest.ontologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changeOntologyMetadataRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => makeTaskFuture(internalOntologyIri = internalOntologyIri) - ) + apiRequestID = changeOntologyMetadataRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => makeTaskFuture(internalOntologyIri = internalOntologyIri) + ) } yield taskResult } def deleteOntologyComment( - deleteOntologyCommentRequestV2: DeleteOntologyCommentRequestV2): Future[ReadOntologyMetadataV2] = { - def makeTaskFuture(internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = { + deleteOntologyCommentRequestV2: DeleteOntologyCommentRequestV2 + ): Future[ReadOntologyMetadataV2] = { + def makeTaskFuture(internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData // Check that the user has permission to update the ontology. - projectIri <- checkPermissionsForOntologyUpdate( - internalOntologyIri = internalOntologyIri, - requestingUser = deleteOntologyCommentRequestV2.requestingUser - ) + projectIri <- OntologyHelpers.checkPermissionsForOntologyUpdate( + internalOntologyIri = internalOntologyIri, + requestingUser = deleteOntologyCommentRequestV2.requestingUser + ) // Check that the ontology exists and has not been updated by another user since the client last read its metadata. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deleteOntologyCommentRequestV2.lastModificationDate, - featureFactoryConfig = deleteOntologyCommentRequestV2.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteOntologyCommentRequestV2.lastModificationDate, + featureFactoryConfig = deleteOntologyCommentRequestV2.featureFactoryConfig + ) // get the metadata of the ontology. oldMetadata: OntologyMetadataV2 = cacheData.ontologies(internalOntologyIri).ontologyMetadata @@ -2244,1439 +801,1377 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changeOntologyMetadata( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - newLabel = None, - hasOldComment = ontologyHasComment, - deleteOldComment = true, - newComment = None, - lastModificationDate = deleteOntologyCommentRequestV2.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changeOntologyMetadata( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + newLabel = None, + hasOldComment = ontologyHasComment, + deleteOldComment = true, + newComment = None, + lastModificationDate = deleteOntologyCommentRequestV2.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the update was successful. unescapedNewMetadata = OntologyMetadataV2( - ontologyIri = internalOntologyIri, - projectIri = Some(projectIri), - label = oldMetadata.label, - comment = None, - lastModificationDate = Some(currentTime) - ).unescape - - maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- loadOntologyMetadata( - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = deleteOntologyCommentRequestV2.featureFactoryConfig - ) + ontologyIri = internalOntologyIri, + projectIri = Some(projectIri), + label = oldMetadata.label, + comment = None, + lastModificationDate = Some(currentTime) + ).unescape + + maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- + OntologyHelpers.loadOntologyMetadata( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = deleteOntologyCommentRequestV2.featureFactoryConfig + ) _ = maybeLoadedOntologyMetadata match { - case Some(loadedOntologyMetadata) => - if (loadedOntologyMetadata != unescapedNewMetadata) { - throw UpdateNotPerformedException() - } + case Some(loadedOntologyMetadata) => + if (loadedOntologyMetadata != unescapedNewMetadata) { + throw UpdateNotPerformedException() + } - case None => throw UpdateNotPerformedException() - } + case None => throw UpdateNotPerformedException() + } // Update the ontology cache with the unescaped metadata. - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> cacheData - .ontologies(internalOntologyIri) - .copy(ontologyMetadata = unescapedNewMetadata)) - )) + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> cacheData + .ontologies(internalOntologyIri) + .copy(ontologyMetadata = unescapedNewMetadata)) + ) + ) } yield ReadOntologyMetadataV2(ontologies = Set(unescapedNewMetadata)) - } for { - _ <- checkExternalOntologyIriForUpdate(deleteOntologyCommentRequestV2.ontologyIri) + _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(deleteOntologyCommentRequestV2.ontologyIri) internalOntologyIri = deleteOntologyCommentRequestV2.ontologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deleteOntologyCommentRequestV2.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => makeTaskFuture(internalOntologyIri = internalOntologyIri) - ) + apiRequestID = deleteOntologyCommentRequestV2.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => makeTaskFuture(internalOntologyIri = internalOntologyIri) + ) } yield taskResult } /** - * Creates a class in an existing ontology. - * - * @param createClassRequest the request to create the class. - * @return a [[ReadOntologyV2]] in the internal schema, the containing the definition of the new class. - */ + * Creates a class in an existing ontology. + * + * @param createClassRequest the request to create the class. + * @return a [[ReadOntologyV2]] in the internal schema, the containing the definition of the new class. + */ private def createClass(createClassRequest: CreateClassRequestV2): Future[ReadOntologyV2] = { def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData internalClassDef: ClassInfoContentV2 = createClassRequest.classInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = createClassRequest.lastModificationDate, - featureFactoryConfig = createClassRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = createClassRequest.lastModificationDate, + featureFactoryConfig = createClassRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. - rdfType: SmartIri = internalClassDef.requireIriObject(OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified")) + rdfType: SmartIri = internalClassDef.requireIriObject( + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } ontology = cacheData.ontologies(internalOntologyIri) // Check that the class doesn't exist yet. _ = if (ontology.classes.contains(internalClassIri)) { - throw BadRequestException(s"Class ${createClassRequest.classInfoContent.classIri} already exists") - } + throw BadRequestException(s"Class ${createClassRequest.classInfoContent.classIri} already exists") + } // Check that the class's IRI isn't already used for something else. _ = if (ontology.properties.contains(internalClassIri) || ontology.individuals.contains(internalClassIri)) { - throw BadRequestException(s"IRI ${createClassRequest.classInfoContent.classIri} is already used") - } + throw BadRequestException(s"IRI ${createClassRequest.classInfoContent.classIri} is already used") + } // Check that the base classes that have Knora IRIs are defined as Knora resource classes. - missingBaseClasses = internalClassDef.subClassOf - .filter(_.isKnoraInternalEntityIri) - .filter(baseClassIri => !isKnoraInternalResourceClass(baseClassIri, cacheData)) + missingBaseClasses = + internalClassDef.subClassOf + .filter(_.isKnoraInternalEntityIri) + .filter(baseClassIri => !OntologyHelpers.isKnoraInternalResourceClass(baseClassIri, cacheData)) _ = if (missingBaseClasses.nonEmpty) { - throw BadRequestException( - s"One or more specified base classes are invalid: ${missingBaseClasses.mkString(", ")}") - } + throw BadRequestException( + s"One or more specified base classes are invalid: ${missingBaseClasses.mkString(", ")}" + ) + } // Check for rdfs:subClassOf cycles. allBaseClassIrisWithoutSelf: Set[SmartIri] = internalClassDef.subClassOf.flatMap { baseClassIri => - cacheData.subClassOfRelations.getOrElse(baseClassIri, Set.empty[SmartIri]).toSet - } + cacheData.subClassOfRelations + .getOrElse(baseClassIri, Set.empty[SmartIri]) + .toSet + } _ = if (allBaseClassIrisWithoutSelf.contains(internalClassIri)) { - throw BadRequestException( - s"Class ${createClassRequest.classInfoContent.classIri} would have a cyclical rdfs:subClassOf") - } + throw BadRequestException( + s"Class ${createClassRequest.classInfoContent.classIri} would have a cyclical rdfs:subClassOf" + ) + } // Check that the class is a subclass of knora-base:Resource. allBaseClassIris: Seq[SmartIri] = internalClassIri +: allBaseClassIrisWithoutSelf.toSeq _ = if (!allBaseClassIris.contains(OntologyConstants.KnoraBase.Resource.toSmartIri)) { - throw BadRequestException( - s"Class ${createClassRequest.classInfoContent.classIri} would not be a subclass of knora-api:Resource") - } + throw BadRequestException( + s"Class ${createClassRequest.classInfoContent.classIri} would not be a subclass of knora-api:Resource" + ) + } // Check that the cardinalities are valid, and add any inherited cardinalities. - (internalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = checkCardinalitiesBeforeAdding( - internalClassDef = internalClassDef, - allBaseClassIris = allBaseClassIris.toSet, - cacheData = cacheData - ) + (internalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = + OntologyHelpers + .checkCardinalitiesBeforeAdding( + internalClassDef = internalClassDef, + allBaseClassIris = allBaseClassIris.toSet, + cacheData = cacheData + ) // Check that the class definition doesn't refer to any non-shared ontologies in other projects. - _ = checkOntologyReferencesInClassDef( - ontologyCacheData = cacheData, - classDef = internalClassDefWithLinkValueProps, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + _ = Cache.checkOntologyReferencesInClassDef( + ontologyCacheData = cacheData, + classDef = internalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // Prepare to update the ontology cache, undoing the SPARQL-escaping of the input. propertyIrisOfAllCardinalitiesForClass = cardinalitiesForClassWithInheritance.keySet - inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = cardinalitiesForClassWithInheritance.filterNot { - case (propertyIri, _) => internalClassDefWithLinkValueProps.directCardinalities.contains(propertyIri) - } + inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = + cardinalitiesForClassWithInheritance.filterNot { case (propertyIri, _) => + internalClassDefWithLinkValueProps.directCardinalities.contains(propertyIri) + } unescapedClassDefWithLinkValueProps = internalClassDefWithLinkValueProps.unescape readClassInfo = ReadClassInfoV2( - entityInfoContent = unescapedClassDefWithLinkValueProps, - allBaseClasses = allBaseClassIris, - isResourceClass = true, - canBeInstantiated = true, - inheritedCardinalities = inheritedCardinalities, - knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - isKnoraResourceProperty(propertyIri, cacheData)), - linkProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isLinkProp(propertyIri, cacheData)), - linkValueProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isLinkValueProp(propertyIri, cacheData)), - fileValueProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isFileValueProp(propertyIri, cacheData)) - ) + entityInfoContent = unescapedClassDefWithLinkValueProps, + allBaseClasses = allBaseClassIris, + isResourceClass = true, + canBeInstantiated = true, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + ), + linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkProp(propertyIri, cacheData) + ), + linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkValueProp(propertyIri, cacheData) + ), + fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isFileValueProp(propertyIri, cacheData) + ) + ) // Add the SPARQL-escaped class to the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .createClass( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classDef = internalClassDefWithLinkValueProps, - lastModificationDate = createClassRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .createClass( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classDef = internalClassDefWithLinkValueProps, + lastModificationDate = createClassRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = createClassRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = createClassRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. - loadedClassDef <- loadClassDefinition( - classIri = internalClassIri, - featureFactoryConfig = createClassRequest.featureFactoryConfig - ) + loadedClassDef <- OntologyHelpers.loadClassDefinition( + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = createClassRequest.featureFactoryConfig + ) _ = if (loadedClassDef != unescapedClassDefWithLinkValueProps) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $unescapedClassDefWithLinkValueProps, but $loadedClassDef was saved") - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $unescapedClassDefWithLinkValueProps, but $loadedClassDef was saved" + ) + } // Update the cache. - updatedSubClassOfRelations = cacheData.subClassOfRelations + (internalClassIri -> allBaseClassIris) - updatedSuperClassOfRelations = calculateSuperClassOfRelations(updatedSubClassOfRelations) + updatedSubClassOfRelations = cacheData.subClassOfRelations + (internalClassIri -> allBaseClassIris) + updatedSuperClassOfRelations = OntologyHelpers.calculateSuperClassOfRelations(updatedSubClassOfRelations) updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> readClassInfo) - ) - - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subClassOfRelations = updatedSubClassOfRelations, - superClassOfRelations = updatedSuperClassOfRelations - )) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> readClassInfo) + ) + + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), + subClassOfRelations = updatedSubClassOfRelations, + superClassOfRelations = updatedSuperClassOfRelations + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = createClassRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = createClassRequest.requestingUser + ) } yield response } for { requestingUser <- FastFuture.successful(createClassRequest.requestingUser) - externalClassIri = createClassRequest.classInfoContent.classIri + externalClassIri = createClassRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = createClassRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = createClassRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Before creating a new class or adding cardinalities to an existing class, checks the validity of the - * cardinalities directly defined on the class. Adds link value properties for the corresponding - * link properties. - * - * @param internalClassDef the internal definition of the class. - * @param allBaseClassIris the IRIs of all the class's base classes, including the class itself. - * @param cacheData the ontology cache. - * @param existingLinkPropsToKeep the link properties that are already defined on the class and that - * will be kept after the update. - * @return the updated class definition, and the cardinalities resulting from inheritance. - */ - private def checkCardinalitiesBeforeAdding( - internalClassDef: ClassInfoContentV2, - allBaseClassIris: Set[SmartIri], - cacheData: OntologyCacheData, - existingLinkPropsToKeep: Set[SmartIri] = Set.empty): (ClassInfoContentV2, Map[SmartIri, KnoraCardinalityInfo]) = { - // If the class has cardinalities, check that the properties are already defined as Knora properties. - - val propertyDefsForDirectCardinalities: Set[ReadPropertyInfoV2] = internalClassDef.directCardinalities.keySet.map { - propertyIri => - if (!isKnoraResourceProperty(propertyIri, cacheData) || propertyIri.toString == OntologyConstants.KnoraBase.ResourceProperty || propertyIri.toString == OntologyConstants.KnoraBase.HasValue) { - throw NotFoundException(s"Invalid property for cardinality: <${propertyIri.toOntologySchema(ApiV2Complex)}>") - } - - cacheData.ontologies(propertyIri.getOntologyFromEntity).properties(propertyIri) - } - - val existingLinkValuePropsToKeep = existingLinkPropsToKeep.map(_.fromLinkPropToLinkValueProp) - val newLinkPropsInClass: Set[SmartIri] = propertyDefsForDirectCardinalities - .filter(_.isLinkProp) - .map(_.entityInfoContent.propertyIri) -- existingLinkValuePropsToKeep - val newLinkValuePropsInClass: Set[SmartIri] = propertyDefsForDirectCardinalities - .filter(_.isLinkValueProp) - .map(_.entityInfoContent.propertyIri) -- existingLinkValuePropsToKeep + * Changes GUI orders in cardinalities in a class definition. + * + * @param changeGuiOrderRequest the request message. + * @return the updated class definition. + */ + private def changeGuiOrder(changeGuiOrderRequest: ChangeGuiOrderRequestV2): Future[ReadOntologyV2] = { + def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { + for { + cacheData <- Cache.getCacheData + internalClassDef: ClassInfoContentV2 = changeGuiOrderRequest.classInfoContent.toOntologySchema(InternalSchema) - // Don't allow link value prop cardinalities to be included in the request. + // Check that the ontology exists and has not been updated by another user since the client last read it. + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changeGuiOrderRequest.lastModificationDate, + featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig + ) - if (newLinkValuePropsInClass.nonEmpty) { - throw BadRequestException(s"In class ${internalClassDef.classIri.toOntologySchema(ApiV2Complex)}, cardinalities have been submitted for one or more link value properties: ${newLinkValuePropsInClass - .map(_.toOntologySchema(ApiV2Complex)) - .mkString(", ")}. Just submit the link properties, and the link value properties will be included automatically.") - } + // Check that the class's rdf:type is owl:Class. - // Add a link value prop cardinality for each new link prop cardinality. + rdfType: SmartIri = internalClassDef.requireIriObject( + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) - val linkValuePropCardinalitiesToAdd: Map[SmartIri, KnoraCardinalityInfo] = newLinkPropsInClass.map { linkPropIri => - val linkValuePropIri = linkPropIri.fromLinkPropToLinkValueProp + _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } - // Ensure that the link value prop exists. - cacheData - .ontologies(linkValuePropIri.getOntologyFromEntity) - .properties - .getOrElse(linkValuePropIri, throw NotFoundException(s"Link value property <$linkValuePropIri> not found")) + // Check that the class exists. - linkValuePropIri -> internalClassDef.directCardinalities(linkPropIri) - }.toMap + ontology = cacheData.ontologies(internalOntologyIri) - val classDefWithAddedLinkValueProps: ClassInfoContentV2 = internalClassDef.copy( - directCardinalities = internalClassDef.directCardinalities ++ linkValuePropCardinalitiesToAdd - ) + currentReadClassInfo: ReadClassInfoV2 = + ontology.classes + .getOrElse( + internalClassIri, + throw BadRequestException(s"Class ${changeGuiOrderRequest.classInfoContent.classIri} does not exist") + ) - // Get the cardinalities that the class can inherit. + // Check that the properties submitted already have cardinalities. - val cardinalitiesAvailableToInherit: Map[SmartIri, KnoraCardinalityInfo] = - classDefWithAddedLinkValueProps.subClassOf.flatMap { baseClassIri => - cacheData.ontologies(baseClassIri.getOntologyFromEntity).classes(baseClassIri).allCardinalities - }.toMap + wrongProperties: Set[SmartIri] = + internalClassDef.directCardinalities.keySet -- currentReadClassInfo.entityInfoContent.directCardinalities.keySet - // Check that the cardinalities directly defined on the class are compatible with any inheritable - // cardinalities, and let directly-defined cardinalities override cardinalities in base classes. - - val cardinalitiesForClassWithInheritance: Map[SmartIri, KnoraCardinalityInfo] = overrideCardinalities( - classIri = internalClassDef.classIri, - thisClassCardinalities = classDefWithAddedLinkValueProps.directCardinalities, - inheritableCardinalities = cardinalitiesAvailableToInherit, - allSubPropertyOfRelations = cacheData.subPropertyOfRelations, - errorSchema = ApiV2Complex, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + _ = if (wrongProperties.nonEmpty) { + throw BadRequestException( + s"One or more submitted properties do not have cardinalities in class ${changeGuiOrderRequest.classInfoContent.classIri}: ${wrongProperties + .map(_.toOntologySchema(ApiV2Complex)) + .mkString(", ")}" + ) + } - // Check that the class is a subclass of all the classes that are subject class constraints of the Knora resource properties in its cardinalities. + linkValuePropCardinalities = internalClassDef.directCardinalities.filter { + case (propertyIri: SmartIri, _: KnoraCardinalityInfo) => + val propertyDef = cacheData + .ontologies(propertyIri.getOntologyFromEntity) + .properties(propertyIri) + propertyDef.isLinkProp + }.map { + case ( + propertyIri: SmartIri, + cardinalityWithCurrentGuiOrder: KnoraCardinalityInfo + ) => + propertyIri.fromLinkPropToLinkValueProp -> cardinalityWithCurrentGuiOrder + } - val knoraResourcePropertyIrisInCardinalities = cardinalitiesForClassWithInheritance.keySet.filter { propertyIri => - isKnoraResourceProperty( - propertyIri = propertyIri, - cacheData = cacheData - ) - } + internalClassDefWithLinkValueProps = internalClassDef.directCardinalities ++ linkValuePropCardinalities - val allClassCardinalityKnoraPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = - knoraResourcePropertyIrisInCardinalities.map { propertyIri => - propertyIri -> cacheData.ontologies(propertyIri.getOntologyFromEntity).properties(propertyIri).entityInfoContent - }.toMap + // Make an updated class definition. - checkSubjectClassConstraintsViaCardinalities( - internalClassDef = classDefWithAddedLinkValueProps, - allBaseClassIris = allBaseClassIris, - allClassCardinalityKnoraPropertyDefs = allClassCardinalityKnoraPropertyDefs, - errorSchema = ApiV2Complex, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + newReadClassInfo = + currentReadClassInfo.copy( + entityInfoContent = currentReadClassInfo.entityInfoContent.copy( + directCardinalities = currentReadClassInfo.entityInfoContent.directCardinalities.map { + case (propertyIri: SmartIri, cardinalityWithCurrentGuiOrder: KnoraCardinalityInfo) => + internalClassDefWithLinkValueProps.get(propertyIri) match { + case Some(cardinalityWithNewGuiOrder) => + propertyIri -> cardinalityWithCurrentGuiOrder.copy(guiOrder = cardinalityWithNewGuiOrder.guiOrder) - // It cannot have cardinalities both on property P and on a subproperty of P. + case None => propertyIri -> cardinalityWithCurrentGuiOrder + } + } + ) + ) - val maybePropertyAndSubproperty: Option[(SmartIri, SmartIri)] = findPropertyAndSubproperty( - propertyIris = cardinalitiesForClassWithInheritance.keySet, - subPropertyOfRelations = cacheData.subPropertyOfRelations - ) + // Replace the cardinalities in the class definition in the triplestore. - maybePropertyAndSubproperty match { - case Some((basePropertyIri, propertyIri)) => - throw BadRequestException( - s"Class <${classDefWithAddedLinkValueProps.classIri.toOntologySchema(ApiV2Complex)}> has a cardinality on property <${basePropertyIri - .toOntologySchema(ApiV2Complex)}> and on its subproperty <${propertyIri.toOntologySchema(ApiV2Complex)}>") + currentTime: Instant = Instant.now - case None => () - } + updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .replaceClassCardinalities( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + newCardinalities = newReadClassInfo.entityInfoContent.directCardinalities, + lastModificationDate = changeGuiOrderRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() - // Check for invalid cardinalities on boolean properties. - checkForInvalidBooleanCardinalities( - classIri = internalClassDef.classIri, - directCardinalities = internalClassDef.directCardinalities, - allPropertyDefs = cacheData.allPropertyDefs, - schemaForErrors = ApiV2Complex, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] - (classDefWithAddedLinkValueProps, cardinalitiesForClassWithInheritance) - } + // Check that the ontology's last modification date was updated. - /** - * Given the IRI of a base class, updates inherited cardinalities in subclasses. - * - * @param baseClassIri the internal IRI of the base class. - * @param cacheData the ontology cache. - * - * @return the updated ontology cache. - */ - private def updateSubClasses(baseClassIri: SmartIri, cacheData: OntologyCacheData): OntologyCacheData = { - // Get the class definitions of all the subclasses of the base class. - - val allSubClassIris: Set[SmartIri] = cacheData.superClassOfRelations(baseClassIri) - - val allSubClasses: Set[ReadClassInfoV2] = allSubClassIris.map { subClassIri => - cacheData.ontologies(subClassIri.getOntologyFromEntity).classes(subClassIri) - } + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig + ) - // Filter them to get only the direct subclasses. + // Check that the data that was saved corresponds to the data that was submitted. - val directSubClasses: Set[ReadClassInfoV2] = allSubClasses.filter { subClass => - subClass.entityInfoContent.subClassOf - .contains(baseClassIri) && subClass.entityInfoContent.classIri != baseClassIri - } + loadedClassDef: ClassInfoContentV2 <- OntologyHelpers.loadClassDefinition( + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig + ) - // Iterate over the subclasses, updating cardinalities. - val cacheDataWithUpdatedSubClasses = directSubClasses.foldLeft(cacheData) { - case (cacheDataAcc: OntologyCacheData, directSubClass: ReadClassInfoV2) => - val directSubClassIri = directSubClass.entityInfoContent.classIri - - // Get the cardinalities that this subclass can inherit from its direct base classes. - - val inheritableCardinalities: Map[SmartIri, KnoraCardinalityInfo] = - directSubClass.entityInfoContent.subClassOf.flatMap { baseClassIri => - cacheData.ontologies(baseClassIri.getOntologyFromEntity).classes(baseClassIri).allCardinalities - }.toMap - - // Override inherited cardinalities with directly defined cardinalities. - val newInheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = overrideCardinalities( - classIri = directSubClassIri, - thisClassCardinalities = directSubClass.entityInfoContent.directCardinalities, - inheritableCardinalities = inheritableCardinalities, - allSubPropertyOfRelations = cacheData.subPropertyOfRelations, - errorSchema = ApiV2Complex, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + _ = if (loadedClassDef != newReadClassInfo.entityInfoContent) { + throw InconsistentRepositoryDataException( + s"Attempted to save class definition ${newReadClassInfo.entityInfoContent}, but $loadedClassDef was saved" + ) + } // Update the cache. - val ontologyIri = directSubClass.entityInfoContent.classIri.getOntologyFromEntity - val ontology: ReadOntologyV2 = cacheDataAcc.ontologies(ontologyIri) + updatedOntology = ontology.copy( + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> newReadClassInfo) + ) - val updatedOntology = ontology.copy( - classes = ontology.classes + (directSubClassIri -> directSubClass.copy( - inheritedCardinalities = newInheritedCardinalities - )) - ) + // Update subclasses and write the cache. - cacheDataAcc.copy( - ontologies = cacheDataAcc.ontologies + (ontologyIri -> updatedOntology) - ) - } + _ = Cache.storeCacheData( + Cache.updateSubClasses( + baseClassIri = internalClassIri, + cacheData = cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) + ) - // Recurse to subclasses of subclasses. + // Read the data back from the cache. - directSubClasses.map(_.entityInfoContent.classIri).foldLeft(cacheDataWithUpdatedSubClasses) { - case (cacheDataAcc: OntologyCacheData, directSubClassIri: SmartIri) => - updateSubClasses(baseClassIri = directSubClassIri, cacheDataAcc) - } - } + response <- getClassDefinitionsFromOntologyV2( + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = changeGuiOrderRequest.requestingUser + ) - /** - * Given a set of property IRIs, determines whether the set contains a property P and a subproperty of P. - * - * @param propertyIris the set of property IRIs. - * @param subPropertyOfRelations all the subproperty relations in the triplestore. - * @return a property and its subproperty, if found. - */ - private def findPropertyAndSubproperty( - propertyIris: Set[SmartIri], - subPropertyOfRelations: Map[SmartIri, Set[SmartIri]]): Option[(SmartIri, SmartIri)] = { - propertyIris.flatMap { propertyIri => - val maybeBasePropertyIri: Option[SmartIri] = (propertyIris - propertyIri).find { otherPropertyIri => - subPropertyOfRelations.get(propertyIri).exists { baseProperties: Set[SmartIri] => - baseProperties.contains(otherPropertyIri) - } - } + } yield response + } - maybeBasePropertyIri.map { basePropertyIri => - (basePropertyIri, propertyIri) - } - }.headOption - } + for { + requestingUser <- FastFuture.successful(changeGuiOrderRequest.requestingUser) - /** - * Checks that a class is a subclass of all the classes that are subject class constraints of the Knora resource properties in its cardinalities. - * - * @param internalClassDef the class definition. - * @param allBaseClassIris the IRIs of all the class's base classes. - * @param allClassCardinalityKnoraPropertyDefs the definitions of all the Knora resource properties on which the class has cardinalities (whether directly defined - * or inherited). - * @param errorSchema the ontology schema to be used in error messages. - * @param errorFun a function that throws an exception. It will be called with an error message argument if the cardinalities are invalid. - */ - private def checkSubjectClassConstraintsViaCardinalities( - internalClassDef: ClassInfoContentV2, - allBaseClassIris: Set[SmartIri], - allClassCardinalityKnoraPropertyDefs: Map[SmartIri, PropertyInfoContentV2], - errorSchema: OntologySchema, - errorFun: String => Nothing): Unit = { - allClassCardinalityKnoraPropertyDefs.foreach { - case (propertyIri, propertyDef) => - propertyDef.predicates.get(OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { - case Some(subjectClassConstraintPred) => - val subjectClassConstraint = subjectClassConstraintPred.requireIriObject( - throw InconsistentRepositoryDataException( - s"Property $propertyIri has an invalid object for ${OntologyConstants.KnoraBase.SubjectClassConstraint}")) + externalClassIri = changeGuiOrderRequest.classInfoContent.classIri + externalOntologyIri = externalClassIri.getOntologyFromEntity - if (!allBaseClassIris.contains(subjectClassConstraint)) { - val hasOrWouldInherit = if (internalClassDef.directCardinalities.contains(propertyIri)) { - "has" - } else { - "would inherit" - } + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - errorFun(s"Class ${internalClassDef.classIri.toOntologySchema(errorSchema)} $hasOrWouldInherit a cardinality for property ${propertyIri - .toOntologySchema(errorSchema)}, but is not a subclass of that property's ${OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri - .toOntologySchema(errorSchema)}, ${subjectClassConstraint.toOntologySchema(errorSchema)}") - } + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) - case None => () - } - } + // Do the remaining pre-update checks and the update while holding a global ontology cache lock. + taskResult <- IriLocker.runWithIriLock( + apiRequestID = changeGuiOrderRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) + } yield taskResult } /** - * Adds cardinalities to an existing class definition. - * - * @param addCardinalitiesRequest the request to add the cardinalities. - * @return a [[ReadOntologyV2]] in the internal schema, containing the new class definition. - */ + * Adds cardinalities to an existing class definition. + * + * @param addCardinalitiesRequest the request to add the cardinalities. + * @return a [[ReadOntologyV2]] in the internal schema, containing the new class definition. + */ private def addCardinalitiesToClass( - addCardinalitiesRequest: AddCardinalitiesToClassRequestV2): Future[ReadOntologyV2] = { + addCardinalitiesRequest: AddCardinalitiesToClassRequestV2 + ): Future[ReadOntologyV2] = { def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData internalClassDef: ClassInfoContentV2 = addCardinalitiesRequest.classInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = addCardinalitiesRequest.lastModificationDate, - featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = addCardinalitiesRequest.lastModificationDate, + featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. - rdfType: SmartIri = internalClassDef.requireIriObject(OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified")) + rdfType: SmartIri = internalClassDef.requireIriObject( + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that cardinalities were submitted. _ = if (internalClassDef.directCardinalities.isEmpty) { - throw BadRequestException("No cardinalities specified") - } + throw BadRequestException("No cardinalities specified") + } // Check that the class exists, that it's a Knora resource class, and that the submitted cardinalities aren't for properties that already have cardinalities // directly defined on the class. ontology = cacheData.ontologies(internalOntologyIri) - existingReadClassInfo: ReadClassInfoV2 = ontology.classes.getOrElse( - internalClassIri, - throw BadRequestException(s"Class ${addCardinalitiesRequest.classInfoContent.classIri} does not exist")) + existingReadClassInfo: ReadClassInfoV2 = + ontology.classes.getOrElse( + internalClassIri, + throw BadRequestException(s"Class ${addCardinalitiesRequest.classInfoContent.classIri} does not exist") + ) existingClassDef: ClassInfoContentV2 = existingReadClassInfo.entityInfoContent redundantCardinalities = existingClassDef.directCardinalities.keySet - .intersect(internalClassDef.directCardinalities.keySet) + .intersect(internalClassDef.directCardinalities.keySet) _ = if (redundantCardinalities.nonEmpty) { - throw BadRequestException( - s"The cardinalities of ${addCardinalitiesRequest.classInfoContent.classIri} already include the following property or properties: ${redundantCardinalities - .mkString(", ")}") - } + throw BadRequestException( + s"The cardinalities of ${addCardinalitiesRequest.classInfoContent.classIri} already include the following property or properties: ${redundantCardinalities + .mkString(", ")}" + ) + } // Is there any property with minCardinality>0 or Cardinality=1? - hasCardinality: Option[(SmartIri, KnoraCardinalityInfo)] = addCardinalitiesRequest.classInfoContent.directCardinalities - .find { + hasCardinality: Option[(SmartIri, KnoraCardinalityInfo)] = + addCardinalitiesRequest.classInfoContent.directCardinalities.find { case (_, constraint: KnoraCardinalityInfo) => constraint.cardinality == Cardinality.MustHaveSome || constraint.cardinality == Cardinality.MustHaveOne } _ <- hasCardinality match { - // If there is, check that the class isn't used in data, and that it has no subclasses. - case Some((propIri: SmartIri, cardinality: KnoraCardinalityInfo)) => - throwIfEntityIsUsed( - entityIri = internalClassIri, - errorFun = throw BadRequestException( - s"Cardinality ${cardinality.toString} for $propIri cannot be added to class ${addCardinalitiesRequest.classInfoContent.classIri}, because it is used in data or has a subclass"), - ignoreKnoraConstraints = true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. - ) - case None => Future.successful(()) - } + // If there is, check that the class isn't used in data, and that it has no subclasses. + case Some((propIri: SmartIri, cardinality: KnoraCardinalityInfo)) => + throwIfEntityIsUsed( + entityIri = internalClassIri, + errorFun = throw BadRequestException( + s"Cardinality ${cardinality.toString} for $propIri cannot be added to class ${addCardinalitiesRequest.classInfoContent.classIri}, because it is used in data or has a subclass" + ), + ignoreKnoraConstraints = + true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. + ) + case None => Future.successful(()) + } // Make an updated class definition. newInternalClassDef = existingClassDef.copy( - directCardinalities = existingClassDef.directCardinalities ++ internalClassDef.directCardinalities - ) + directCardinalities = + existingClassDef.directCardinalities ++ internalClassDef.directCardinalities + ) // Check that the new cardinalities are valid, and add any inherited cardinalities. allBaseClassIrisWithoutInternal: Seq[SmartIri] = newInternalClassDef.subClassOf.toSeq.flatMap { baseClassIri => - cacheData.subClassOfRelations.getOrElse(baseClassIri, Seq.empty[SmartIri]) - } + cacheData.subClassOfRelations.getOrElse( + baseClassIri, + Seq.empty[SmartIri] + ) + } allBaseClassIris: Seq[SmartIri] = internalClassIri +: allBaseClassIrisWithoutInternal - (newInternalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = checkCardinalitiesBeforeAdding( - internalClassDef = newInternalClassDef, - allBaseClassIris = allBaseClassIris.toSet, - cacheData = cacheData, - existingLinkPropsToKeep = existingReadClassInfo.linkProperties - ) - - // Check that the class definition doesn't refer to any non-shared ontologies in other projects. - _ = checkOntologyReferencesInClassDef( - ontologyCacheData = cacheData, - classDef = newInternalClassDefWithLinkValueProps, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) - - // Prepare to update the ontology cache. (No need to deal with SPARQL-escaping here, because there - // isn't any text to escape in cardinalities.) - - propertyIrisOfAllCardinalitiesForClass = cardinalitiesForClassWithInheritance.keySet - - inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = cardinalitiesForClassWithInheritance.filterNot { - case (propertyIri, _) => newInternalClassDefWithLinkValueProps.directCardinalities.contains(propertyIri) - } - - readClassInfo = ReadClassInfoV2( - entityInfoContent = newInternalClassDefWithLinkValueProps, - allBaseClasses = allBaseClassIris, - isResourceClass = true, - canBeInstantiated = true, - inheritedCardinalities = inheritedCardinalities, - knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - isKnoraResourceProperty(propertyIri, cacheData)), - linkProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isLinkProp(propertyIri, cacheData)), - linkValueProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isLinkValueProp(propertyIri, cacheData)), - fileValueProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isFileValueProp(propertyIri, cacheData)) - ) - - // Add the cardinalities to the class definition in the triplestore. - - currentTime: Instant = Instant.now - - updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .addCardinalitiesToClass( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - cardinalitiesToAdd = newInternalClassDefWithLinkValueProps.directCardinalities, - lastModificationDate = addCardinalitiesRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() - - _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] - - // Check that the ontology's last modification date was updated. - - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig - ) - - // Check that the data that was saved corresponds to the data that was submitted. - - loadedClassDef <- loadClassDefinition( - classIri = internalClassIri, - featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig - ) - - _ = if (loadedClassDef != newInternalClassDefWithLinkValueProps) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved") - } - - // Update subclasses and write the cache. - - updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> readClassInfo) - ) - - _ = storeCacheData( - updateSubClasses(baseClassIri = internalClassIri, - cacheData = cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ))) - - // Read the data back from the cache. - - response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = addCardinalitiesRequest.requestingUser - ) - } yield response - } - - for { - requestingUser <- FastFuture.successful(addCardinalitiesRequest.requestingUser) - - externalClassIri = addCardinalitiesRequest.classInfoContent.classIri - externalOntologyIri = externalClassIri.getOntologyFromEntity - - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) - - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) - internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) - - // Do the remaining pre-update checks and the update while holding a global ontology cache lock. - taskResult <- IriLocker.runWithIriLock( - apiRequestID = addCardinalitiesRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) - } yield taskResult - } - - /** - * Changes GUI orders in cardinalities in a class definition. - * - * @param changeGuiOrderRequest the request message. - * @return the updated class definition. - */ - private def changeGuiOrder(changeGuiOrderRequest: ChangeGuiOrderRequestV2): Future[ReadOntologyV2] = { - def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { - for { - cacheData <- getCacheData - internalClassDef: ClassInfoContentV2 = changeGuiOrderRequest.classInfoContent.toOntologySchema(InternalSchema) - - // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changeGuiOrderRequest.lastModificationDate, - featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig - ) - - // Check that the class's rdf:type is owl:Class. - - rdfType: SmartIri = internalClassDef.requireIriObject(OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified")) - - _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } - - // Check that the class exists. - - ontology = cacheData.ontologies(internalOntologyIri) - - currentReadClassInfo: ReadClassInfoV2 = ontology.classes - .getOrElse( - internalClassIri, - throw BadRequestException(s"Class ${changeGuiOrderRequest.classInfoContent.classIri} does not exist")) - - // Check that the properties submitted already have cardinalities. - - wrongProperties: Set[SmartIri] = internalClassDef.directCardinalities.keySet -- currentReadClassInfo.entityInfoContent.directCardinalities.keySet + (newInternalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = + OntologyHelpers + .checkCardinalitiesBeforeAdding( + internalClassDef = newInternalClassDef, + allBaseClassIris = allBaseClassIris.toSet, + cacheData = cacheData, + existingLinkPropsToKeep = existingReadClassInfo.linkProperties + ) - _ = if (wrongProperties.nonEmpty) { - throw BadRequestException( - s"One or more submitted properties do not have cardinalities in class ${changeGuiOrderRequest.classInfoContent.classIri}: ${wrongProperties - .map(_.toOntologySchema(ApiV2Complex)) - .mkString(", ")}") - } + // Check that the class definition doesn't refer to any non-shared ontologies in other projects. + _ = Cache.checkOntologyReferencesInClassDef( + ontologyCacheData = cacheData, + classDef = newInternalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) - linkValuePropCardinalities = internalClassDef.directCardinalities - .filter { - case (propertyIri: SmartIri, _: KnoraCardinalityInfo) => - val propertyDef = cacheData.ontologies(propertyIri.getOntologyFromEntity).properties(propertyIri) - propertyDef.isLinkProp - } - .map { - case (propertyIri: SmartIri, cardinalityWithCurrentGuiOrder: KnoraCardinalityInfo) => - propertyIri.fromLinkPropToLinkValueProp -> cardinalityWithCurrentGuiOrder - } + // Prepare to update the ontology cache. (No need to deal with SPARQL-escaping here, because there + // isn't any text to escape in cardinalities.) - internalClassDefWithLinkValueProps = internalClassDef.directCardinalities ++ linkValuePropCardinalities + propertyIrisOfAllCardinalitiesForClass = cardinalitiesForClassWithInheritance.keySet - // Make an updated class definition. + inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = + cardinalitiesForClassWithInheritance.filterNot { case (propertyIri, _) => + newInternalClassDefWithLinkValueProps.directCardinalities.contains(propertyIri) + } - newReadClassInfo = currentReadClassInfo.copy( - entityInfoContent = currentReadClassInfo.entityInfoContent.copy( - directCardinalities = currentReadClassInfo.entityInfoContent.directCardinalities.map { - case (propertyIri: SmartIri, cardinalityWithCurrentGuiOrder: KnoraCardinalityInfo) => - internalClassDefWithLinkValueProps.get(propertyIri) match { - case Some(cardinalityWithNewGuiOrder) => - propertyIri -> cardinalityWithCurrentGuiOrder.copy(guiOrder = cardinalityWithNewGuiOrder.guiOrder) - - case None => propertyIri -> cardinalityWithCurrentGuiOrder - } - } - ) - ) + readClassInfo = ReadClassInfoV2( + entityInfoContent = newInternalClassDefWithLinkValueProps, + allBaseClasses = allBaseClassIris, + isResourceClass = true, + canBeInstantiated = true, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + ), + linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkProp(propertyIri, cacheData) + ), + linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkValueProp(propertyIri, cacheData) + ), + fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isFileValueProp(propertyIri, cacheData) + ) + ) - // Replace the cardinalities in the class definition in the triplestore. + // Add the cardinalities to the class definition in the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .replaceClassCardinalities( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - newCardinalities = newReadClassInfo.entityInfoContent.directCardinalities, - lastModificationDate = changeGuiOrderRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .addCardinalitiesToClass( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + cardinalitiesToAdd = newInternalClassDefWithLinkValueProps.directCardinalities, + lastModificationDate = addCardinalitiesRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. - loadedClassDef: ClassInfoContentV2 <- loadClassDefinition( - classIri = internalClassIri, - featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig - ) - - _ = if (loadedClassDef != newReadClassInfo.entityInfoContent) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition ${newReadClassInfo.entityInfoContent}, but $loadedClassDef was saved") - } - - // Update the cache. + loadedClassDef <- OntologyHelpers.loadClassDefinition( + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig + ) - updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> newReadClassInfo) - ) + _ = if (loadedClassDef != newInternalClassDefWithLinkValueProps) { + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" + ) + } // Update subclasses and write the cache. - _ = storeCacheData( - updateSubClasses(baseClassIri = internalClassIri, - cacheData = cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ))) + updatedOntology = ontology.copy( + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> readClassInfo) + ) + + _ = Cache.storeCacheData( + Cache.updateSubClasses( + baseClassIri = internalClassIri, + cacheData = cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = changeGuiOrderRequest.requestingUser - ) - + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = addCardinalitiesRequest.requestingUser + ) } yield response } for { - requestingUser <- FastFuture.successful(changeGuiOrderRequest.requestingUser) + requestingUser <- FastFuture.successful(addCardinalitiesRequest.requestingUser) - externalClassIri = changeGuiOrderRequest.classInfoContent.classIri + externalClassIri = addCardinalitiesRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changeGuiOrderRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = addCardinalitiesRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Checks whether a class's cardinalities can be replaced. - * - * @param canChangeCardinalitiesRequest the request message. - * @return a [[CanDoResponseV2]] indicating whether a class's cardinalities can be replaced. - */ + * Checks whether a class's cardinalities can be replaced. + * + * @param canChangeCardinalitiesRequest the request message. + * @return a [[CanDoResponseV2]] indicating whether a class's cardinalities can be replaced. + */ private def canChangeClassCardinalities( - canChangeCardinalitiesRequest: CanChangeCardinalitiesRequestV2): Future[CanDoResponseV2] = { - val internalClassIri: SmartIri = canChangeCardinalitiesRequest.classIri.toOntologySchema(InternalSchema) + canChangeCardinalitiesRequest: CanChangeCardinalitiesRequestV2 + ): Future[CanDoResponseV2] = { + val internalClassIri: SmartIri = canChangeCardinalitiesRequest.classIri.toOntologySchema(InternalSchema) val internalOntologyIri: SmartIri = internalClassIri.getOntologyFromEntity for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData ontology = cacheData.ontologies.getOrElse( - internalOntologyIri, - throw BadRequestException( - s"Ontology ${canChangeCardinalitiesRequest.classIri.getOntologyFromEntity} does not exist")) + internalOntologyIri, + throw BadRequestException( + s"Ontology ${canChangeCardinalitiesRequest.classIri.getOntologyFromEntity} does not exist" + ) + ) _ = if (!ontology.classes.contains(internalClassIri)) { - throw BadRequestException(s"Class ${canChangeCardinalitiesRequest.classIri} does not exist") - } + throw BadRequestException(s"Class ${canChangeCardinalitiesRequest.classIri} does not exist") + } - userCanUpdateOntology <- canUserUpdateOntology(internalOntologyIri, canChangeCardinalitiesRequest.requestingUser) + userCanUpdateOntology <- + OntologyHelpers.canUserUpdateOntology(internalOntologyIri, canChangeCardinalitiesRequest.requestingUser) classIsUsed <- isEntityUsed( - entityIri = internalClassIri, - ignoreKnoraConstraints = true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. - ) + entityIri = internalClassIri, + ignoreKnoraConstraints = + true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. + ) } yield CanDoResponseV2(userCanUpdateOntology && !classIsUsed) } /** - * Replaces a class's cardinalities with new ones. - * - * @param changeCardinalitiesRequest the request to add the cardinalities. - * @return a [[ReadOntologyV2]] in the internal schema, containing the new class definition. - */ + * Replaces a class's cardinalities with new ones. + * + * @param changeCardinalitiesRequest the request to add the cardinalities. + * @return a [[ReadOntologyV2]] in the internal schema, containing the new class definition. + */ private def changeClassCardinalities( - changeCardinalitiesRequest: ChangeCardinalitiesRequestV2): Future[ReadOntologyV2] = { + changeCardinalitiesRequest: ChangeCardinalitiesRequestV2 + ): Future[ReadOntologyV2] = { def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- getCacheData - internalClassDef: ClassInfoContentV2 = changeCardinalitiesRequest.classInfoContent.toOntologySchema( - InternalSchema) + cacheData <- Cache.getCacheData + internalClassDef: ClassInfoContentV2 = + changeCardinalitiesRequest.classInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changeCardinalitiesRequest.lastModificationDate, - featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changeCardinalitiesRequest.lastModificationDate, + featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. - rdfType: SmartIri = internalClassDef.requireIriObject(OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified")) + rdfType: SmartIri = internalClassDef.requireIriObject( + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that the class exists. ontology = cacheData.ontologies(internalOntologyIri) - existingClassDef: ClassInfoContentV2 = ontology.classes - .getOrElse( - internalClassIri, - throw BadRequestException(s"Class ${changeCardinalitiesRequest.classInfoContent.classIri} does not exist")) - .entityInfoContent + existingClassDef: ClassInfoContentV2 = + ontology.classes + .getOrElse( + internalClassIri, + throw BadRequestException(s"Class ${changeCardinalitiesRequest.classInfoContent.classIri} does not exist") + ) + .entityInfoContent // Check that the class isn't used in data, and that it has no subclasses. + // TODO: If class is used in data, check additionally if the property(ies) being removed is(are) truly used and if not, then allow. _ <- throwIfEntityIsUsed( - entityIri = internalClassIri, - errorFun = throw BadRequestException( - s"The cardinalities of class ${changeCardinalitiesRequest.classInfoContent.classIri} cannot be changed, because it is used in data or has a subclass"), - ignoreKnoraConstraints = true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. - ) + entityIri = internalClassIri, + errorFun = throw BadRequestException( + s"The cardinalities of class ${changeCardinalitiesRequest.classInfoContent.classIri} cannot be changed, because it is used in data or has a subclass" + ), + ignoreKnoraConstraints = + true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. + ) // Make an updated class definition. newInternalClassDef = existingClassDef.copy( - directCardinalities = internalClassDef.directCardinalities - ) + directCardinalities = internalClassDef.directCardinalities + ) - // Check that the new cardinalities are valid, and add any inherited cardinalities. + // Check that the new cardinalities are valid, and don't add any inherited cardinalities. allBaseClassIrisWithoutInternal: Seq[SmartIri] = newInternalClassDef.subClassOf.toSeq.flatMap { baseClassIri => - cacheData.subClassOfRelations.getOrElse(baseClassIri, Seq.empty[SmartIri]) - } + cacheData.subClassOfRelations.getOrElse( + baseClassIri, + Seq.empty[SmartIri] + ) + } allBaseClassIris: Seq[SmartIri] = internalClassIri +: allBaseClassIrisWithoutInternal - (newInternalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = checkCardinalitiesBeforeAdding( - internalClassDef = newInternalClassDef, - allBaseClassIris = allBaseClassIris.toSet, - cacheData = cacheData - ) + (newInternalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = + OntologyHelpers + .checkCardinalitiesBeforeAdding( + internalClassDef = newInternalClassDef, + allBaseClassIris = allBaseClassIris.toSet, + cacheData = cacheData + ) // Check that the class definition doesn't refer to any non-shared ontologies in other projects. - _ = checkOntologyReferencesInClassDef( - ontologyCacheData = cacheData, - classDef = newInternalClassDefWithLinkValueProps, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + _ = Cache.checkOntologyReferencesInClassDef( + ontologyCacheData = cacheData, + classDef = newInternalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // Prepare to update the ontology cache. (No need to deal with SPARQL-escaping here, because there // isn't any text to escape in cardinalities.) propertyIrisOfAllCardinalitiesForClass = cardinalitiesForClassWithInheritance.keySet - inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = cardinalitiesForClassWithInheritance.filterNot { - case (propertyIri, _) => newInternalClassDefWithLinkValueProps.directCardinalities.contains(propertyIri) - } + inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = + cardinalitiesForClassWithInheritance.filterNot { case (propertyIri, _) => + newInternalClassDefWithLinkValueProps.directCardinalities.contains(propertyIri) + } readClassInfo = ReadClassInfoV2( - entityInfoContent = newInternalClassDefWithLinkValueProps, - allBaseClasses = allBaseClassIris, - isResourceClass = true, - canBeInstantiated = true, - inheritedCardinalities = inheritedCardinalities, - knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - isKnoraResourceProperty(propertyIri, cacheData)), - linkProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isLinkProp(propertyIri, cacheData)), - linkValueProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isLinkValueProp(propertyIri, cacheData)), - fileValueProperties = - propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => isFileValueProp(propertyIri, cacheData)) - ) + entityInfoContent = newInternalClassDefWithLinkValueProps, + allBaseClasses = allBaseClassIris, + isResourceClass = true, + canBeInstantiated = true, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + ), + linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkProp(propertyIri, cacheData) + ), + linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkValueProp(propertyIri, cacheData) + ), + fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isFileValueProp(propertyIri, cacheData) + ) + ) // Add the cardinalities to the class definition in the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .replaceClassCardinalities( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - newCardinalities = newInternalClassDefWithLinkValueProps.directCardinalities, - lastModificationDate = changeCardinalitiesRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .replaceClassCardinalities( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + newCardinalities = newInternalClassDefWithLinkValueProps.directCardinalities, + lastModificationDate = changeCardinalitiesRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. - loadedClassDef <- loadClassDefinition( - classIri = internalClassIri, - featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig - ) + loadedClassDef <- OntologyHelpers.loadClassDefinition( + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig + ) _ = if (loadedClassDef != newInternalClassDefWithLinkValueProps) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved") - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" + ) + } // Update the cache. updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> readClassInfo) - ) - - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - )) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> readClassInfo) + ) + + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = changeCardinalitiesRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = changeCardinalitiesRequest.requestingUser + ) } yield response } for { requestingUser <- FastFuture.successful(changeCardinalitiesRequest.requestingUser) - externalClassIri = changeCardinalitiesRequest.classInfoContent.classIri + externalClassIri = changeCardinalitiesRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changeCardinalitiesRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changeCardinalitiesRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Checks whether a class can be deleted. - * - * @param canDeleteClassRequest the request message. - * @return a [[CanDoResponseV2]]. - */ + * FIXME(DSP-1856): Only works if a single cardinality is supplied. + * Checks if cardinalities can be removed from a class. + * + * @param canDeleteCardinalitiesFromClassRequest the request to remove cardinalities. + * @return a [[ReadOntologyV2]] in the internal schema, containing the new class definition. + */ + private def canDeleteCardinalitiesFromClass( + canDeleteCardinalitiesFromClassRequest: CanDeleteCardinalitiesFromClassRequestV2 + ): Future[CanDoResponseV2] = + for { + requestingUser <- FastFuture.successful(canDeleteCardinalitiesFromClassRequest.requestingUser) + + externalClassIri = canDeleteCardinalitiesFromClassRequest.classInfoContent.classIri + externalOntologyIri = externalClassIri.getOntologyFromEntity + + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) + + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) + + // Do the remaining pre-update checks and the update while holding a global ontology cache lock. + taskResult <- IriLocker.runWithIriLock( + apiRequestID = canDeleteCardinalitiesFromClassRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + Cardinalities.canDeleteCardinalitiesFromClass( + settings, + storeManager, + deleteCardinalitiesFromClassRequest = canDeleteCardinalitiesFromClassRequest, + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) + } yield taskResult + + /** + * FIXME(DSP-1856): Only works if a single cardinality is supplied. + * Removes cardinalities (from class to properties) from class if properties are not used inside data. + * + * @param deleteCardinalitiesFromClassRequest the request to remove cardinalities. + * @return a [[ReadOntologyV2]] in the internal schema, containing the new class definition. + */ + private def deleteCardinalitiesFromClass( + deleteCardinalitiesFromClassRequest: DeleteCardinalitiesFromClassRequestV2 + ): Future[ReadOntologyV2] = + for { + requestingUser <- FastFuture.successful(deleteCardinalitiesFromClassRequest.requestingUser) + + externalClassIri = deleteCardinalitiesFromClassRequest.classInfoContent.classIri + externalOntologyIri = externalClassIri.getOntologyFromEntity + + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) + + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) + + // Do the remaining pre-update checks and the update while holding a global ontology cache lock. + taskResult <- IriLocker.runWithIriLock( + apiRequestID = deleteCardinalitiesFromClassRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + Cardinalities.deleteCardinalitiesFromClass( + settings, + storeManager, + deleteCardinalitiesFromClassRequest = deleteCardinalitiesFromClassRequest, + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) + } yield taskResult + + /** + * Checks whether a class can be deleted. + * + * @param canDeleteClassRequest the request message. + * @return a [[CanDoResponseV2]]. + */ private def canDeleteClass(canDeleteClassRequest: CanDeleteClassRequestV2): Future[CanDoResponseV2] = { - val internalClassIri: SmartIri = canDeleteClassRequest.classIri.toOntologySchema(InternalSchema) + val internalClassIri: SmartIri = canDeleteClassRequest.classIri.toOntologySchema(InternalSchema) val internalOntologyIri: SmartIri = internalClassIri.getOntologyFromEntity for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData - ontology = cacheData.ontologies.getOrElse( - internalOntologyIri, - throw BadRequestException(s"Ontology ${canDeleteClassRequest.classIri.getOntologyFromEntity} does not exist")) + ontology = + cacheData.ontologies.getOrElse( + internalOntologyIri, + throw BadRequestException(s"Ontology ${canDeleteClassRequest.classIri.getOntologyFromEntity} does not exist") + ) _ = if (!ontology.classes.contains(internalClassIri)) { - throw BadRequestException(s"Class ${canDeleteClassRequest.classIri} does not exist") - } + throw BadRequestException(s"Class ${canDeleteClassRequest.classIri} does not exist") + } - userCanUpdateOntology <- canUserUpdateOntology(internalOntologyIri, canDeleteClassRequest.requestingUser) + userCanUpdateOntology <- + OntologyHelpers.canUserUpdateOntology(internalOntologyIri, canDeleteClassRequest.requestingUser) classIsUsed <- isEntityUsed(entityIri = internalClassIri) } yield CanDoResponseV2(userCanUpdateOntology && !classIsUsed) } /** - * Deletes a class. - * - * @param deleteClassRequest the request to delete the class. - * @return a [[SuccessResponseV2]]. - */ + * Deletes a class. + * + * @param deleteClassRequest the request to delete the class. + * @return a [[SuccessResponseV2]]. + */ private def deleteClass(deleteClassRequest: DeleteClassRequestV2): Future[ReadOntologyMetadataV2] = { - def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = { + def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deleteClassRequest.lastModificationDate, - featureFactoryConfig = deleteClassRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteClassRequest.lastModificationDate, + featureFactoryConfig = deleteClassRequest.featureFactoryConfig + ) // Check that the class exists. ontology = cacheData.ontologies(internalOntologyIri) _ = if (!ontology.classes.contains(internalClassIri)) { - throw BadRequestException(s"Class ${deleteClassRequest.classIri} does not exist") - } + throw BadRequestException(s"Class ${deleteClassRequest.classIri} does not exist") + } // Check that the class isn't used in data or ontologies. _ <- throwIfEntityIsUsed( - entityIri = internalClassIri, - errorFun = throw BadRequestException( - s"Class ${deleteClassRequest.classIri} cannot be deleted, because it is used in data or ontologies") - ) + entityIri = internalClassIri, + errorFun = throw BadRequestException( + s"Class ${deleteClassRequest.classIri} cannot be deleted, because it is used in data or ontologies" + ) + ) // Delete the class from the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .deleteClass( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - lastModificationDate = deleteClassRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .deleteClass( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + lastModificationDate = deleteClassRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = deleteClassRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = deleteClassRequest.featureFactoryConfig + ) // Update the cache. updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes - internalClassIri - ) - - updatedSubClassOfRelations = (cacheData.subClassOfRelations - internalClassIri).map { - case (subClass, baseClasses) => subClass -> (baseClasses.toSet - internalClassIri).toSeq - } + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes - internalClassIri + ) + + updatedSubClassOfRelations = + (cacheData.subClassOfRelations - internalClassIri).map { case (subClass, baseClasses) => + subClass -> (baseClasses.toSet - internalClassIri).toSeq + } - updatedSuperClassOfRelations = calculateSuperClassOfRelations(updatedSubClassOfRelations) + updatedSuperClassOfRelations = OntologyHelpers.calculateSuperClassOfRelations(updatedSubClassOfRelations) - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subClassOfRelations = updatedSubClassOfRelations, - superClassOfRelations = updatedSuperClassOfRelations - )) + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), + subClassOfRelations = updatedSubClassOfRelations, + superClassOfRelations = updatedSuperClassOfRelations + ) + ) } yield ReadOntologyMetadataV2(Set(updatedOntology.ontologyMetadata)) - } for { requestingUser <- FastFuture.successful(deleteClassRequest.requestingUser) - externalClassIri = deleteClassRequest.classIri + externalClassIri = deleteClassRequest.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deleteClassRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = deleteClassRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Checks whether a property can be deleted. - * - * @param canDeletePropertyRequest the request message. - * @return a [[CanDoResponseV2]] indicating whether the property can be deleted. - */ + * Checks whether a property can be deleted. + * + * @param canDeletePropertyRequest the request message. + * @return a [[CanDoResponseV2]] indicating whether the property can be deleted. + */ private def canDeleteProperty(canDeletePropertyRequest: CanDeletePropertyRequestV2): Future[CanDoResponseV2] = { val internalPropertyIri = canDeletePropertyRequest.propertyIri.toOntologySchema(InternalSchema) val internalOntologyIri = internalPropertyIri.getOntologyFromEntity for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData ontology = cacheData.ontologies.getOrElse( - internalOntologyIri, - throw BadRequestException( - s"Ontology ${canDeletePropertyRequest.propertyIri.getOntologyFromEntity} does not exist")) - - propertyDef: ReadPropertyInfoV2 = ontology.properties.getOrElse( - internalPropertyIri, - throw BadRequestException(s"Property ${canDeletePropertyRequest.propertyIri} does not exist")) + internalOntologyIri, + throw BadRequestException( + s"Ontology ${canDeletePropertyRequest.propertyIri.getOntologyFromEntity} does not exist" + ) + ) + + propertyDef: ReadPropertyInfoV2 = + ontology.properties.getOrElse( + internalPropertyIri, + throw BadRequestException(s"Property ${canDeletePropertyRequest.propertyIri} does not exist") + ) _ = if (propertyDef.isLinkValueProp) { - throw BadRequestException( - s"A link value property cannot be deleted directly; check the corresponding link property instead") - } + throw BadRequestException( + s"A link value property cannot be deleted directly; check the corresponding link property instead" + ) + } - userCanUpdateOntology <- canUserUpdateOntology(internalOntologyIri, canDeletePropertyRequest.requestingUser) + userCanUpdateOntology <- + OntologyHelpers.canUserUpdateOntology(internalOntologyIri, canDeletePropertyRequest.requestingUser) propertyIsUsed <- isEntityUsed(internalPropertyIri) } yield CanDoResponseV2(userCanUpdateOntology && !propertyIsUsed) } /** - * Deletes a property. If the property is a link property, the corresponding link value property is also deleted. - * - * @param deletePropertyRequest the request to delete the property. - * @return a [[ReadOntologyMetadataV2]]. - */ + * Deletes a property. If the property is a link property, the corresponding link value property is also deleted. + * + * @param deletePropertyRequest the request to delete the property. + * @return a [[ReadOntologyMetadataV2]]. + */ private def deleteProperty(deletePropertyRequest: DeletePropertyRequestV2): Future[ReadOntologyMetadataV2] = { - def makeTaskFuture(internalPropertyIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = { + def makeTaskFuture(internalPropertyIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyMetadataV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deletePropertyRequest.lastModificationDate, - featureFactoryConfig = deletePropertyRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deletePropertyRequest.lastModificationDate, + featureFactoryConfig = deletePropertyRequest.featureFactoryConfig + ) // Check that the property exists. ontology = cacheData.ontologies(internalOntologyIri) - propertyDef: ReadPropertyInfoV2 = ontology.properties.getOrElse( - internalPropertyIri, - throw BadRequestException(s"Property ${deletePropertyRequest.propertyIri} does not exist")) + propertyDef: ReadPropertyInfoV2 = + ontology.properties.getOrElse( + internalPropertyIri, + throw BadRequestException(s"Property ${deletePropertyRequest.propertyIri} does not exist") + ) _ = if (propertyDef.isLinkValueProp) { - throw BadRequestException( - s"A link value property cannot be deleted directly; delete the corresponding link property instead") - } + throw BadRequestException( + s"A link value property cannot be deleted directly; delete the corresponding link property instead" + ) + } maybeInternalLinkValuePropertyIri: Option[SmartIri] = if (propertyDef.isLinkProp) { - Some(internalPropertyIri.fromLinkPropToLinkValueProp) - } else { - None - } + Some(internalPropertyIri.fromLinkPropToLinkValueProp) + } else { + None + } // Check that the property isn't used in data or ontologies. _ <- throwIfEntityIsUsed( - entityIri = internalPropertyIri, - errorFun = throw BadRequestException( - s"Property ${deletePropertyRequest.propertyIri} cannot be deleted, because it is used in data or ontologies") - ) + entityIri = internalPropertyIri, + errorFun = throw BadRequestException( + s"Property ${deletePropertyRequest.propertyIri} cannot be deleted, because it is used in data or ontologies" + ) + ) _ <- maybeInternalLinkValuePropertyIri match { - case Some(internalLinkValuePropertyIri) => - throwIfEntityIsUsed( - entityIri = internalLinkValuePropertyIri, - errorFun = throw BadRequestException( - s"Property ${deletePropertyRequest.propertyIri} cannot be deleted, because the corresponding link value property, ${internalLinkValuePropertyIri - .toOntologySchema(ApiV2Complex)}, is used in data or ontologies") - ) - - case None => FastFuture.successful(()) - } + case Some(internalLinkValuePropertyIri) => + throwIfEntityIsUsed( + entityIri = internalLinkValuePropertyIri, + errorFun = throw BadRequestException( + s"Property ${deletePropertyRequest.propertyIri} cannot be deleted, because the corresponding link value property, ${internalLinkValuePropertyIri + .toOntologySchema(ApiV2Complex)}, is used in data or ontologies" + ) + ) + + case None => FastFuture.successful(()) + } // Delete the property from the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .deleteProperty( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - propertyIri = internalPropertyIri, - maybeLinkValuePropertyIri = maybeInternalLinkValuePropertyIri, - lastModificationDate = deletePropertyRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .deleteProperty( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + propertyIri = internalPropertyIri, + maybeLinkValuePropertyIri = maybeInternalLinkValuePropertyIri, + lastModificationDate = deletePropertyRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = deletePropertyRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = deletePropertyRequest.featureFactoryConfig + ) // Update the cache. propertiesToRemoveFromCache = Set(internalPropertyIri) ++ maybeInternalLinkValuePropertyIri updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - properties = ontology.properties -- propertiesToRemoveFromCache - ) - - updatedSubPropertyOfRelations = (cacheData.subPropertyOfRelations -- propertiesToRemoveFromCache).map { - case (subProperty, baseProperties) => subProperty -> (baseProperties -- propertiesToRemoveFromCache) - } + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + properties = ontology.properties -- propertiesToRemoveFromCache + ) + + updatedSubPropertyOfRelations = + (cacheData.subPropertyOfRelations -- propertiesToRemoveFromCache).map { case (subProperty, baseProperties) => + subProperty -> (baseProperties -- propertiesToRemoveFromCache) + } - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subPropertyOfRelations = updatedSubPropertyOfRelations - )) + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), + subPropertyOfRelations = updatedSubPropertyOfRelations + ) + ) } yield ReadOntologyMetadataV2(Set(updatedOntology.ontologyMetadata)) - } for { requestingUser <- FastFuture.successful(deletePropertyRequest.requestingUser) @@ -3684,339 +2179,367 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon externalPropertyIri = deletePropertyRequest.propertyIri externalOntologyIri = externalPropertyIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalPropertyIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalPropertyIri, + requestingUser = requestingUser + ) internalPropertyIri = externalPropertyIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deletePropertyRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalPropertyIri = internalPropertyIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = deletePropertyRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalPropertyIri = internalPropertyIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Checks whether an ontology can be deleted. - * - * @param canDeleteOntologyRequest the request message. - * @return a [[CanDoResponseV2]] indicating whether an ontology can be deleted. - */ + * Checks whether an ontology can be deleted. + * + * @param canDeleteOntologyRequest the request message. + * @return a [[CanDoResponseV2]] indicating whether an ontology can be deleted. + */ private def canDeleteOntology(canDeleteOntologyRequest: CanDeleteOntologyRequestV2): Future[CanDoResponseV2] = { val internalOntologyIri: SmartIri = canDeleteOntologyRequest.ontologyIri.toOntologySchema(InternalSchema) for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData ontology = cacheData.ontologies.getOrElse( - internalOntologyIri, - throw BadRequestException( - s"Ontology ${canDeleteOntologyRequest.ontologyIri.getOntologyFromEntity} does not exist")) - - userCanUpdateOntology <- canUserUpdateOntology(internalOntologyIri, canDeleteOntologyRequest.requestingUser) - subjectsUsingOntology <- getSubjectsUsingOntology(ontology) + internalOntologyIri, + throw BadRequestException( + s"Ontology ${canDeleteOntologyRequest.ontologyIri.getOntologyFromEntity} does not exist" + ) + ) + + userCanUpdateOntology <- + OntologyHelpers.canUserUpdateOntology(internalOntologyIri, canDeleteOntologyRequest.requestingUser) + subjectsUsingOntology <- OntologyHelpers.getSubjectsUsingOntology(settings, storeManager, ontology) } yield CanDoResponseV2(userCanUpdateOntology && subjectsUsingOntology.isEmpty) } - /** - * Gets the set of subjects that refer to an ontology or its entities. - * - * @param ontology the ontology. - * @return the set of subjects that refer to the ontology or its entities. - */ - private def getSubjectsUsingOntology(ontology: ReadOntologyV2): Future[Set[IRI]] = { - for { - isOntologyUsedSparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .isOntologyUsed( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = ontology.ontologyMetadata.ontologyIri, - classIris = ontology.classes.keySet, - propertyIris = ontology.properties.keySet - ) - .toString()) - - isOntologyUsedResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(isOntologyUsedSparql)) - .mapTo[SparqlSelectResult] - - subjects = isOntologyUsedResponse.results.bindings.map { row => - row.rowMap("s") - }.toSet - } yield subjects - } - private def deleteOntology(deleteOntologyRequest: DeleteOntologyRequestV2): Future[SuccessResponseV2] = { - def makeTaskFuture(internalOntologyIri: SmartIri): Future[SuccessResponseV2] = { + def makeTaskFuture(internalOntologyIri: SmartIri): Future[SuccessResponseV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData // Check that the user has permission to update the ontology. - _ <- checkPermissionsForOntologyUpdate( - internalOntologyIri = internalOntologyIri, - requestingUser = deleteOntologyRequest.requestingUser - ) + _ <- OntologyHelpers.checkPermissionsForOntologyUpdate( + internalOntologyIri = internalOntologyIri, + requestingUser = deleteOntologyRequest.requestingUser + ) // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deleteOntologyRequest.lastModificationDate, - featureFactoryConfig = deleteOntologyRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteOntologyRequest.lastModificationDate, + featureFactoryConfig = deleteOntologyRequest.featureFactoryConfig + ) // Check that none of the entities in the ontology are used in data or in other ontologies. - ontology = cacheData.ontologies(internalOntologyIri) - subjectsUsingOntology: Set[IRI] <- getSubjectsUsingOntology(ontology) + ontology = cacheData.ontologies(internalOntologyIri) + subjectsUsingOntology: Set[IRI] <- OntologyHelpers.getSubjectsUsingOntology(settings, storeManager, ontology) _ = if (subjectsUsingOntology.nonEmpty) { - val sortedSubjects: Seq[IRI] = subjectsUsingOntology.map(s => "<" + s + ">").toVector.sorted + val sortedSubjects: Seq[IRI] = subjectsUsingOntology.map(s => "<" + s + ">").toVector.sorted - throw BadRequestException( - s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} cannot be deleted, because of subjects that refer to it: ${sortedSubjects - .mkString(", ")}") - } + throw BadRequestException( + s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} cannot be deleted, because of subjects that refer to it: ${sortedSubjects + .mkString(", ")}" + ) + } // Delete everything in the ontology's named graph. updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .deleteOntology( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri - ) - .toString() + .deleteOntology( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology has been deleted. - maybeOntologyMetadata <- loadOntologyMetadata( - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = deleteOntologyRequest.featureFactoryConfig - ) + maybeOntologyMetadata <- OntologyHelpers.loadOntologyMetadata( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = deleteOntologyRequest.featureFactoryConfig + ) _ = if (maybeOntologyMetadata.nonEmpty) { - throw UpdateNotPerformedException( - s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} was not deleted. Please report this as a possible bug.") - } + throw UpdateNotPerformedException( + s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} was not deleted. Please report this as a possible bug." + ) + } // Remove the ontology from the cache. - updatedSubClassOfRelations = cacheData.subClassOfRelations - .filterNot { - case (subClass, _) => subClass.getOntologyFromEntity == internalOntologyIri - } - .map { - case (subClass, baseClasses) => - subClass -> baseClasses.filterNot(_.getOntologyFromEntity == internalOntologyIri) - } + updatedSubClassOfRelations = cacheData.subClassOfRelations.filterNot { case (subClass, _) => + subClass.getOntologyFromEntity == internalOntologyIri + }.map { case (subClass, baseClasses) => + subClass -> baseClasses.filterNot(_.getOntologyFromEntity == internalOntologyIri) + } - updatedSuperClassOfRelations = calculateSuperClassOfRelations(updatedSubClassOfRelations) + updatedSuperClassOfRelations = OntologyHelpers.calculateSuperClassOfRelations(updatedSubClassOfRelations) - updatedSubPropertyOfRelations = cacheData.subPropertyOfRelations - .filterNot { - case (subProperty, _) => subProperty.getOntologyFromEntity == internalOntologyIri - } - .map { - case (subProperty, baseProperties) => - subProperty -> baseProperties.filterNot(_.getOntologyFromEntity == internalOntologyIri) - } + updatedSubPropertyOfRelations = cacheData.subPropertyOfRelations.filterNot { case (subProperty, _) => + subProperty.getOntologyFromEntity == internalOntologyIri + }.map { case (subProperty, baseProperties) => + subProperty -> baseProperties.filterNot( + _.getOntologyFromEntity == internalOntologyIri + ) + } - updatedStandoffProperties = cacheData.standoffProperties.filterNot( - _.getOntologyFromEntity == internalOntologyIri) + updatedStandoffProperties = + cacheData.standoffProperties.filterNot(_.getOntologyFromEntity == internalOntologyIri) updatedCacheData = cacheData.copy( - ontologies = cacheData.ontologies - internalOntologyIri, - subClassOfRelations = updatedSubClassOfRelations, - superClassOfRelations = updatedSuperClassOfRelations, - subPropertyOfRelations = updatedSubPropertyOfRelations, - standoffProperties = updatedStandoffProperties - ) - - _ = storeCacheData(updatedCacheData) + ontologies = cacheData.ontologies - internalOntologyIri, + subClassOfRelations = updatedSubClassOfRelations, + superClassOfRelations = updatedSuperClassOfRelations, + subPropertyOfRelations = updatedSubPropertyOfRelations, + standoffProperties = updatedStandoffProperties + ) + + _ = Cache.storeCacheData(updatedCacheData) } yield SuccessResponseV2(s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} has been deleted") - } for { - _ <- checkExternalOntologyIriForUpdate(deleteOntologyRequest.ontologyIri) + _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(deleteOntologyRequest.ontologyIri) internalOntologyIri = deleteOntologyRequest.ontologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deleteOntologyRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = deleteOntologyRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Creates a property in an existing ontology. - * - * @param createPropertyRequest the request to create the property. - * @return a [[ReadOntologyV2]] in the internal schema, the containing the definition of the new property. - */ + * Creates a property in an existing ontology. + * + * @param createPropertyRequest the request to create the property. + * @return a [[ReadOntologyV2]] in the internal schema, the containing the definition of the new property. + */ private def createProperty(createPropertyRequest: CreatePropertyRequestV2): Future[ReadOntologyV2] = { def makeTaskFuture(internalPropertyIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData internalPropertyDef = createPropertyRequest.propertyInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = createPropertyRequest.lastModificationDate, - featureFactoryConfig = createPropertyRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = createPropertyRequest.lastModificationDate, + featureFactoryConfig = createPropertyRequest.featureFactoryConfig + ) // Check that the property's rdf:type is owl:ObjectProperty. - rdfType: SmartIri = internalPropertyDef.requireIriObject(OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified")) + rdfType: SmartIri = internalPropertyDef.requireIriObject( + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.ObjectProperty.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that the property doesn't exist yet. ontology = cacheData.ontologies(internalOntologyIri) _ = if (ontology.properties.contains(internalPropertyIri)) { - throw BadRequestException(s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} already exists") - } + throw BadRequestException( + s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} already exists" + ) + } // Check that the property's IRI isn't already used for something else. _ = if (ontology.classes.contains(internalPropertyIri) || ontology.individuals.contains(internalPropertyIri)) { - throw BadRequestException(s"IRI ${createPropertyRequest.propertyInfoContent.propertyIri} is already used") - } + throw BadRequestException(s"IRI ${createPropertyRequest.propertyInfoContent.propertyIri} is already used") + } // Check that the base properties that have Knora IRIs are defined as Knora resource properties. knoraSuperProperties = internalPropertyDef.subPropertyOf.filter(_.isKnoraInternalEntityIri) invalidSuperProperties = knoraSuperProperties.filterNot(baseProperty => - isKnoraResourceProperty(baseProperty, cacheData) && baseProperty.toString != OntologyConstants.KnoraBase.ResourceProperty) + OntologyHelpers + .isKnoraResourceProperty( + baseProperty, + cacheData + ) && baseProperty.toString != OntologyConstants.KnoraBase.ResourceProperty + ) _ = if (invalidSuperProperties.nonEmpty) { - throw BadRequestException( - s"One or more specified base properties are invalid: ${invalidSuperProperties.mkString(", ")}") - } + throw BadRequestException( + s"One or more specified base properties are invalid: ${invalidSuperProperties.mkString(", ")}" + ) + } // Check for rdfs:subPropertyOf cycles. allKnoraSuperPropertyIrisWithoutSelf: Set[SmartIri] = knoraSuperProperties.flatMap { superPropertyIri => - cacheData.subPropertyOfRelations.getOrElse(superPropertyIri, Set.empty[SmartIri]) - } + cacheData.subPropertyOfRelations.getOrElse( + superPropertyIri, + Set.empty[SmartIri] + ) + } _ = if (allKnoraSuperPropertyIrisWithoutSelf.contains(internalPropertyIri)) { - throw BadRequestException( - s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would have a cyclical rdfs:subPropertyOf") - } + throw BadRequestException( + s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would have a cyclical rdfs:subPropertyOf" + ) + } // Check the property is a subproperty of knora-base:hasValue or knora-base:hasLinkTo, but not both. allKnoraSuperPropertyIris: Set[SmartIri] = allKnoraSuperPropertyIrisWithoutSelf + internalPropertyIri - isValueProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) - isLinkProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri) + isValueProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) + isLinkProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri) isLinkValueProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri) isFileValueProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasFileValue.toSmartIri) _ = if (!(isValueProp || isLinkProp)) { - throw BadRequestException( - s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would not be a subproperty of knora-api:hasValue or knora-api:hasLinkTo") - } + throw BadRequestException( + s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would not be a subproperty of knora-api:hasValue or knora-api:hasLinkTo" + ) + } _ = if (isValueProp && isLinkProp) { - throw BadRequestException( - s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would be a subproperty of both knora-api:hasValue and knora-api:hasLinkTo") - } + throw BadRequestException( + s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would be a subproperty of both knora-api:hasValue and knora-api:hasLinkTo" + ) + } // Don't allow new file value properties to be created. _ = if (isFileValueProp) { - throw BadRequestException("New file value properties cannot be created") - } + throw BadRequestException("New file value properties cannot be created") + } // Don't allow new link value properties to be created directly, because we do that automatically when creating a link property. _ = if (isLinkValueProp) { - throw BadRequestException( - "New link value properties cannot be created directly. Create a link property instead.") - } + throw BadRequestException( + "New link value properties cannot be created directly. Create a link property instead." + ) + } // Check the property's salsah-gui:guiElement and salsah-gui:guiAttribute. - _ = validateGuiAttributes( - propertyInfoContent = internalPropertyDef, - allGuiAttributeDefinitions = cacheData.guiAttributeDefinitions, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + _ = OntologyHelpers.validateGuiAttributes( + propertyInfoContent = internalPropertyDef, + allGuiAttributeDefinitions = cacheData.guiAttributeDefinitions, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // If we're creating a link property, make the definition of the corresponding link value property. maybeLinkValuePropertyDef: Option[PropertyInfoContentV2] = if (isLinkProp) { - val linkValuePropertyDef = linkPropertyDefToLinkValuePropertyDef(internalPropertyDef) - - if (ontology.properties.contains(linkValuePropertyDef.propertyIri)) { - throw BadRequestException(s"Link value property ${linkValuePropertyDef.propertyIri} already exists") - } - - Some(linkValuePropertyDef) - } else { - None - } + val linkValuePropertyDef = OntologyHelpers + .linkPropertyDefToLinkValuePropertyDef( + internalPropertyDef + ) + + if ( + ontology.properties.contains( + linkValuePropertyDef.propertyIri + ) + ) { + throw BadRequestException( + s"Link value property ${linkValuePropertyDef.propertyIri} already exists" + ) + } + + Some(linkValuePropertyDef) + } else { + None + } // Check that the subject class constraint, if provided, designates a Knora resource class that exists. - maybeSubjectClassConstraintPred: Option[PredicateInfoV2] = internalPropertyDef.predicates.get( - OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) + maybeSubjectClassConstraintPred: Option[PredicateInfoV2] = + internalPropertyDef.predicates.get(OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) maybeSubjectClassConstraint = maybeSubjectClassConstraintPred.map( - _.requireIriObject(throw BadRequestException("Invalid knora-api:subjectType"))) + _.requireIriObject(throw BadRequestException("Invalid knora-api:subjectType")) + ) _ = maybeSubjectClassConstraint.foreach { subjectClassConstraint => - if (!isKnoraInternalResourceClass(subjectClassConstraint, cacheData)) { - throw BadRequestException( - s"Invalid subject class constraint: ${subjectClassConstraint.toOntologySchema(ApiV2Complex)}") - } - } + if (!OntologyHelpers.isKnoraInternalResourceClass(subjectClassConstraint, cacheData)) { + throw BadRequestException( + s"Invalid subject class constraint: ${subjectClassConstraint.toOntologySchema(ApiV2Complex)}" + ) + } + } // Check that the object class constraint designates an appropriate class that exists. objectClassConstraint: SmartIri = internalPropertyDef.requireIriObject( - OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, - throw BadRequestException(s"No knora-api:objectType specified")) + OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, + throw BadRequestException(s"No knora-api:objectType specified") + ) // If this is a value property, ensure its object class constraint is not LinkValue or a file value class. _ = if (!isLinkProp) { - if (objectClassConstraint.toString == OntologyConstants.KnoraBase.LinkValue || - OntologyConstants.KnoraBase.FileValueClasses.contains(objectClassConstraint.toString)) { - throw BadRequestException( - s"Invalid object class constraint for value property: ${objectClassConstraint.toOntologySchema(ApiV2Complex)}") - } - } + if ( + objectClassConstraint.toString == OntologyConstants.KnoraBase.LinkValue || + OntologyConstants.KnoraBase.FileValueClasses.contains(objectClassConstraint.toString) + ) { + throw BadRequestException( + s"Invalid object class constraint for value property: ${objectClassConstraint.toOntologySchema(ApiV2Complex)}" + ) + } + } // Check that the subject class, if provided, is a subclass of the subject classes of the base properties. _ = maybeSubjectClassConstraint match { - case Some(subjectClassConstraint) => - checkPropertyConstraint( + case Some(subjectClassConstraint) => + Cache.checkPropertyConstraint( + cacheData = cacheData, + internalPropertyIri = internalPropertyIri, + constraintPredicateIri = OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri, + constraintValueToBeChecked = subjectClassConstraint, + allSuperPropertyIris = allKnoraSuperPropertyIris, + errorSchema = ApiV2Complex, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) + + case None => () + } + + // Check that the object class is a subclass of the object classes of the base properties. + + _ = Cache.checkPropertyConstraint( cacheData = cacheData, internalPropertyIri = internalPropertyIri, - constraintPredicateIri = OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri, - constraintValueToBeChecked = subjectClassConstraint, + constraintPredicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, + constraintValueToBeChecked = objectClassConstraint, allSuperPropertyIris = allKnoraSuperPropertyIris, errorSchema = ApiV2Complex, errorFun = { msg: String => @@ -4024,106 +2547,97 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } ) - case None => () - } - - // Check that the object class is a subclass of the object classes of the base properties. - - _ = checkPropertyConstraint( - cacheData = cacheData, - internalPropertyIri = internalPropertyIri, - constraintPredicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, - constraintValueToBeChecked = objectClassConstraint, - allSuperPropertyIris = allKnoraSuperPropertyIris, - errorSchema = ApiV2Complex, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) - // Check that the property definition doesn't refer to any non-shared ontologies in other projects. - _ = checkOntologyReferencesInPropertyDef( - ontologyCacheData = cacheData, - propertyDef = internalPropertyDef, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + _ = Cache.checkOntologyReferencesInPropertyDef( + ontologyCacheData = cacheData, + propertyDef = internalPropertyDef, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // Add the property (and the link value property if needed) to the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .createProperty( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - propertyDef = internalPropertyDef, - maybeLinkValuePropertyDef = maybeLinkValuePropertyDef, - lastModificationDate = createPropertyRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .createProperty( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + propertyDef = internalPropertyDef, + maybeLinkValuePropertyDef = maybeLinkValuePropertyDef, + lastModificationDate = createPropertyRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = createPropertyRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = createPropertyRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. To make this comparison, // we have to undo the SPARQL-escaping of the input. - loadedPropertyDef <- loadPropertyDefinition( - propertyIri = internalPropertyIri, - featureFactoryConfig = createPropertyRequest.featureFactoryConfig - ) + loadedPropertyDef <- OntologyHelpers.loadPropertyDefinition( + settings, + storeManager, + propertyIri = internalPropertyIri, + featureFactoryConfig = createPropertyRequest.featureFactoryConfig + ) unescapedInputPropertyDef = internalPropertyDef.unescape _ = if (loadedPropertyDef != unescapedInputPropertyDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save property definition $unescapedInputPropertyDef, but $loadedPropertyDef was saved") - } + throw InconsistentRepositoryDataException( + s"Attempted to save property definition $unescapedInputPropertyDef, but $loadedPropertyDef was saved" + ) + } - maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = maybeLinkValuePropertyDef.map { - linkValuePropertyDef => - loadPropertyDefinition( + maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = + maybeLinkValuePropertyDef.map { linkValuePropertyDef => + OntologyHelpers.loadPropertyDefinition( + settings, + storeManager, propertyIri = linkValuePropertyDef.propertyIri, featureFactoryConfig = createPropertyRequest.featureFactoryConfig ) - } + } - maybeLoadedLinkValuePropertyDef: Option[PropertyInfoContentV2] <- ActorUtil.optionFuture2FutureOption( - maybeLoadedLinkValuePropertyDefFuture) + maybeLoadedLinkValuePropertyDef: Option[PropertyInfoContentV2] <- + ActorUtil.optionFuture2FutureOption(maybeLoadedLinkValuePropertyDefFuture) maybeUnescapedNewLinkValuePropertyDef = maybeLinkValuePropertyDef.map(_.unescape) - _ = (maybeLoadedLinkValuePropertyDef, maybeUnescapedNewLinkValuePropertyDef) match { - case (Some(loadedLinkValuePropertyDef), Some(unescapedNewLinkPropertyDef)) => - if (loadedLinkValuePropertyDef != unescapedNewLinkPropertyDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save link value property definition $unescapedNewLinkPropertyDef, but $loadedLinkValuePropertyDef was saved") + _ = (maybeLoadedLinkValuePropertyDef, maybeUnescapedNewLinkValuePropertyDef) match { + case (Some(loadedLinkValuePropertyDef), Some(unescapedNewLinkPropertyDef)) => + if (loadedLinkValuePropertyDef != unescapedNewLinkPropertyDef) { + throw InconsistentRepositoryDataException( + s"Attempted to save link value property definition $unescapedNewLinkPropertyDef, but $loadedLinkValuePropertyDef was saved" + ) + } + + case _ => () } - case _ => () - } - // Update the ontology cache, using the unescaped definition(s). readPropertyInfo = ReadPropertyInfoV2( - entityInfoContent = unescapedInputPropertyDef, - isEditable = true, - isResourceProp = true, - isLinkProp = isLinkProp - ) - - maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = maybeUnescapedNewLinkValuePropertyDef - .map { unescapedNewLinkPropertyDef => + entityInfoContent = unescapedInputPropertyDef, + isEditable = true, + isResourceProp = true, + isLinkProp = isLinkProp + ) + + maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = + maybeUnescapedNewLinkValuePropertyDef.map { unescapedNewLinkPropertyDef => unescapedNewLinkPropertyDef.propertyIri -> ReadPropertyInfoV2( entityInfoContent = unescapedNewLinkPropertyDef, isResourceProp = true, @@ -4132,27 +2646,31 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } updatedOntologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ) - - updatedOntology = ontology.copy( - ontologyMetadata = updatedOntologyMetadata, - properties = ontology.properties ++ maybeLinkValuePropertyCacheEntry + (internalPropertyIri -> readPropertyInfo) - ) + lastModificationDate = Some(currentTime) + ) + + updatedOntology = + ontology.copy( + ontologyMetadata = updatedOntologyMetadata, + properties = + ontology.properties ++ maybeLinkValuePropertyCacheEntry + (internalPropertyIri -> readPropertyInfo) + ) - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subPropertyOfRelations = cacheData.subPropertyOfRelations + (internalPropertyIri -> allKnoraSuperPropertyIris) - )) + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), + subPropertyOfRelations = + cacheData.subPropertyOfRelations + (internalPropertyIri -> allKnoraSuperPropertyIris) + ) + ) // Read the data back from the cache. response <- getPropertyDefinitionsFromOntologyV2( - propertyIris = Set(internalPropertyIri), - allLanguages = true, - requestingUser = createPropertyRequest.requestingUser - ) + propertyIris = Set(internalPropertyIri), + allLanguages = true, + requestingUser = createPropertyRequest.requestingUser + ) } yield response } @@ -4162,145 +2680,166 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon externalPropertyIri = createPropertyRequest.propertyInfoContent.propertyIri externalOntologyIri = externalPropertyIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalPropertyIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalPropertyIri, + requestingUser = requestingUser + ) internalPropertyIri = externalPropertyIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = createPropertyRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalPropertyIri = internalPropertyIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = createPropertyRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalPropertyIri = internalPropertyIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Changes the values of `salsah-gui:guiElement` and `salsah-gui:guiAttribute` in a property definition. - * - * @param changePropertyGuiElementRequest the request to change the property's GUI element and GUI attribute. - * @return a [[ReadOntologyV2]] containing the modified property definition. - */ + * Changes the values of `salsah-gui:guiElement` and `salsah-gui:guiAttribute` in a property definition. + * + * @param changePropertyGuiElementRequest the request to change the property's GUI element and GUI attribute. + * @return a [[ReadOntologyV2]] containing the modified property definition. + */ private def changePropertyGuiElement( - changePropertyGuiElementRequest: ChangePropertyGuiElementRequest): Future[ReadOntologyV2] = { + changePropertyGuiElementRequest: ChangePropertyGuiElementRequest + ): Future[ReadOntologyV2] = { def makeTaskFuture(internalPropertyIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData ontology = cacheData.ontologies(internalOntologyIri) - currentReadPropertyInfo: ReadPropertyInfoV2 = ontology.properties.getOrElse( - internalPropertyIri, - throw NotFoundException(s"Property ${changePropertyGuiElementRequest.propertyIri} not found")) + currentReadPropertyInfo: ReadPropertyInfoV2 = + ontology.properties.getOrElse( + internalPropertyIri, + throw NotFoundException(s"Property ${changePropertyGuiElementRequest.propertyIri} not found") + ) // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changePropertyGuiElementRequest.lastModificationDate, - featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changePropertyGuiElementRequest.lastModificationDate, + featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig + ) // If this is a link property, also change the GUI element and attribute of the corresponding link value property. maybeCurrentLinkValueReadPropertyInfo: Option[ReadPropertyInfoV2] = if (currentReadPropertyInfo.isLinkProp) { - val linkValuePropertyIri = internalPropertyIri.fromLinkPropToLinkValueProp - Some( - ontology.properties.getOrElse( - linkValuePropertyIri, - throw InconsistentRepositoryDataException(s"Link value property $linkValuePropertyIri not found"))) - } else { - None - } + val linkValuePropertyIri = + internalPropertyIri.fromLinkPropToLinkValueProp + Some( + ontology.properties.getOrElse( + linkValuePropertyIri, + throw InconsistentRepositoryDataException( + s"Link value property $linkValuePropertyIri not found" + ) + ) + ) + } else { + None + } // Do the update. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changePropertyGuiElement( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - propertyIri = internalPropertyIri, - maybeLinkValuePropertyIri = maybeCurrentLinkValueReadPropertyInfo.map(_.entityInfoContent.propertyIri), - maybeNewGuiElement = changePropertyGuiElementRequest.newGuiElement, - newGuiAttributes = changePropertyGuiElementRequest.newGuiAttributes, - lastModificationDate = changePropertyGuiElementRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changePropertyGuiElement( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + propertyIri = internalPropertyIri, + maybeLinkValuePropertyIri = + maybeCurrentLinkValueReadPropertyInfo.map(_.entityInfoContent.propertyIri), + maybeNewGuiElement = changePropertyGuiElementRequest.newGuiElement, + newGuiAttributes = changePropertyGuiElementRequest.newGuiAttributes, + lastModificationDate = changePropertyGuiElementRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. To make this comparison, // we have to undo the SPARQL-escaping of the input. - loadedPropertyDef <- loadPropertyDefinition( - propertyIri = internalPropertyIri, - featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig - ) + loadedPropertyDef <- OntologyHelpers.loadPropertyDefinition( + settings, + storeManager, + propertyIri = internalPropertyIri, + featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig + ) - maybeNewGuiElementPredicate: Option[(SmartIri, PredicateInfoV2)] = changePropertyGuiElementRequest.newGuiElement - .map { guiElement: SmartIri => + maybeNewGuiElementPredicate: Option[(SmartIri, PredicateInfoV2)] = + changePropertyGuiElementRequest.newGuiElement.map { guiElement: SmartIri => OntologyConstants.SalsahGui.GuiElementProp.toSmartIri -> PredicateInfoV2( predicateIri = OntologyConstants.SalsahGui.GuiElementProp.toSmartIri, objects = Seq(SmartIriLiteralV2(guiElement)) ) } - maybeUnescapedNewGuiAttributePredicate: Option[(SmartIri, PredicateInfoV2)] = if (changePropertyGuiElementRequest.newGuiAttributes.nonEmpty) { - Some( - OntologyConstants.SalsahGui.GuiAttribute.toSmartIri -> PredicateInfoV2( - predicateIri = OntologyConstants.SalsahGui.GuiAttribute.toSmartIri, - objects = changePropertyGuiElementRequest.newGuiAttributes.map(StringLiteralV2(_)).toSeq - )) - } else { - None - } + maybeUnescapedNewGuiAttributePredicate: Option[(SmartIri, PredicateInfoV2)] = + if (changePropertyGuiElementRequest.newGuiAttributes.nonEmpty) { + Some( + OntologyConstants.SalsahGui.GuiAttribute.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.SalsahGui.GuiAttribute.toSmartIri, + objects = changePropertyGuiElementRequest.newGuiAttributes.map(StringLiteralV2(_)).toSeq + ) + ) + } else { + None + } unescapedNewPropertyDef: PropertyInfoContentV2 = currentReadPropertyInfo.entityInfoContent.copy( - predicates = currentReadPropertyInfo.entityInfoContent.predicates - - OntologyConstants.SalsahGui.GuiElementProp.toSmartIri - - OntologyConstants.SalsahGui.GuiAttribute.toSmartIri ++ - maybeNewGuiElementPredicate ++ - maybeUnescapedNewGuiAttributePredicate - ) + predicates = + currentReadPropertyInfo.entityInfoContent.predicates - + OntologyConstants.SalsahGui.GuiElementProp.toSmartIri - + OntologyConstants.SalsahGui.GuiAttribute.toSmartIri ++ + maybeNewGuiElementPredicate ++ + maybeUnescapedNewGuiAttributePredicate + ) _ = if (loadedPropertyDef != unescapedNewPropertyDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save property definition $unescapedNewPropertyDef, but $loadedPropertyDef was saved") - } + throw InconsistentRepositoryDataException( + s"Attempted to save property definition $unescapedNewPropertyDef, but $loadedPropertyDef was saved" + ) + } - maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = maybeCurrentLinkValueReadPropertyInfo - .map { linkValueReadPropertyInfo => - loadPropertyDefinition( + maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = + maybeCurrentLinkValueReadPropertyInfo.map { linkValueReadPropertyInfo => + OntologyHelpers.loadPropertyDefinition( + settings, + storeManager, propertyIri = linkValueReadPropertyInfo.entityInfoContent.propertyIri, featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig ) } - maybeLoadedLinkValuePropertyDef: Option[PropertyInfoContentV2] <- ActorUtil.optionFuture2FutureOption( - maybeLoadedLinkValuePropertyDefFuture) + maybeLoadedLinkValuePropertyDef: Option[PropertyInfoContentV2] <- + ActorUtil.optionFuture2FutureOption(maybeLoadedLinkValuePropertyDefFuture) - maybeUnescapedNewLinkValuePropertyDef: Option[PropertyInfoContentV2] = maybeLoadedLinkValuePropertyDef.map { - loadedLinkValuePropertyDef => + maybeUnescapedNewLinkValuePropertyDef: Option[PropertyInfoContentV2] = + maybeLoadedLinkValuePropertyDef.map { loadedLinkValuePropertyDef => val unescapedNewLinkPropertyDef = maybeCurrentLinkValueReadPropertyInfo.get.entityInfoContent.copy( predicates = maybeCurrentLinkValueReadPropertyInfo.get.entityInfoContent.predicates - OntologyConstants.SalsahGui.GuiElementProp.toSmartIri - @@ -4311,23 +2850,24 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon if (loadedLinkValuePropertyDef != unescapedNewLinkPropertyDef) { throw InconsistentRepositoryDataException( - s"Attempted to save link value property definition $unescapedNewLinkPropertyDef, but $loadedLinkValuePropertyDef was saved") + s"Attempted to save link value property definition $unescapedNewLinkPropertyDef, but $loadedLinkValuePropertyDef was saved" + ) } unescapedNewLinkPropertyDef - } + } // Update the ontology cache, using the unescaped definition(s). newReadPropertyInfo = ReadPropertyInfoV2( - entityInfoContent = unescapedNewPropertyDef, - isEditable = true, - isResourceProp = true, - isLinkProp = currentReadPropertyInfo.isLinkProp - ) - - maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = maybeUnescapedNewLinkValuePropertyDef - .map { unescapedNewLinkPropertyDef => + entityInfoContent = unescapedNewPropertyDef, + isEditable = true, + isResourceProp = true, + isLinkProp = currentReadPropertyInfo.isLinkProp + ) + + maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = + maybeUnescapedNewLinkValuePropertyDef.map { unescapedNewLinkPropertyDef => unescapedNewLinkPropertyDef.propertyIri -> ReadPropertyInfoV2( entityInfoContent = unescapedNewLinkPropertyDef, isResourceProp = true, @@ -4336,25 +2876,29 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } updatedOntologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ) - - updatedOntology = ontology.copy( - ontologyMetadata = updatedOntologyMetadata, - properties = ontology.properties ++ maybeLinkValuePropertyCacheEntry + (internalPropertyIri -> newReadPropertyInfo) - ) + lastModificationDate = Some(currentTime) + ) + + updatedOntology = + ontology.copy( + ontologyMetadata = updatedOntologyMetadata, + properties = + ontology.properties ++ maybeLinkValuePropertyCacheEntry + (internalPropertyIri -> newReadPropertyInfo) + ) - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - )) + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) // Read the data back from the cache. response <- getPropertyDefinitionsFromOntologyV2( - propertyIris = Set(internalPropertyIri), - allLanguages = true, - requestingUser = changePropertyGuiElementRequest.requestingUser) + propertyIris = Set(internalPropertyIri), + allLanguages = true, + requestingUser = changePropertyGuiElementRequest.requestingUser + ) } yield response } @@ -4364,151 +2908,174 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon externalPropertyIri = changePropertyGuiElementRequest.propertyIri externalOntologyIri = externalPropertyIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalPropertyIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalPropertyIri, + requestingUser = requestingUser + ) internalPropertyIri = externalPropertyIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changePropertyGuiElementRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalPropertyIri = internalPropertyIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changePropertyGuiElementRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalPropertyIri = internalPropertyIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Changes the values of `rdfs:label` or `rdfs:comment` in a property definition. - * - * @param changePropertyLabelsOrCommentsRequest the request to change the property's labels or comments. - * @return a [[ReadOntologyV2]] containing the modified property definition. - */ + * Changes the values of `rdfs:label` or `rdfs:comment` in a property definition. + * + * @param changePropertyLabelsOrCommentsRequest the request to change the property's labels or comments. + * @return a [[ReadOntologyV2]] containing the modified property definition. + */ private def changePropertyLabelsOrComments( - changePropertyLabelsOrCommentsRequest: ChangePropertyLabelsOrCommentsRequestV2): Future[ReadOntologyV2] = { + changePropertyLabelsOrCommentsRequest: ChangePropertyLabelsOrCommentsRequestV2 + ): Future[ReadOntologyV2] = { def makeTaskFuture(internalPropertyIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData ontology = cacheData.ontologies(internalOntologyIri) - currentReadPropertyInfo: ReadPropertyInfoV2 = ontology.properties.getOrElse( - internalPropertyIri, - throw NotFoundException(s"Property ${changePropertyLabelsOrCommentsRequest.propertyIri} not found")) + currentReadPropertyInfo: ReadPropertyInfoV2 = + ontology.properties.getOrElse( + internalPropertyIri, + throw NotFoundException(s"Property ${changePropertyLabelsOrCommentsRequest.propertyIri} not found") + ) // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changePropertyLabelsOrCommentsRequest.lastModificationDate, - featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changePropertyLabelsOrCommentsRequest.lastModificationDate, + featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig + ) // If this is a link property, also change the labels/comments of the corresponding link value property. maybeCurrentLinkValueReadPropertyInfo: Option[ReadPropertyInfoV2] = if (currentReadPropertyInfo.isLinkProp) { - val linkValuePropertyIri = internalPropertyIri.fromLinkPropToLinkValueProp - Some( - ontology.properties.getOrElse( - linkValuePropertyIri, - throw InconsistentRepositoryDataException(s"Link value property $linkValuePropertyIri not found"))) - } else { - None - } + val linkValuePropertyIri = + internalPropertyIri.fromLinkPropToLinkValueProp + Some( + ontology.properties.getOrElse( + linkValuePropertyIri, + throw InconsistentRepositoryDataException( + s"Link value property $linkValuePropertyIri not found" + ) + ) + ) + } else { + None + } // Do the update. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changePropertyLabelsOrComments( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - propertyIri = internalPropertyIri, - maybeLinkValuePropertyIri = maybeCurrentLinkValueReadPropertyInfo.map(_.entityInfoContent.propertyIri), - predicateToUpdate = changePropertyLabelsOrCommentsRequest.predicateToUpdate, - newObjects = changePropertyLabelsOrCommentsRequest.newObjects, - lastModificationDate = changePropertyLabelsOrCommentsRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changePropertyLabelsOrComments( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + propertyIri = internalPropertyIri, + maybeLinkValuePropertyIri = + maybeCurrentLinkValueReadPropertyInfo.map(_.entityInfoContent.propertyIri), + predicateToUpdate = changePropertyLabelsOrCommentsRequest.predicateToUpdate, + newObjects = changePropertyLabelsOrCommentsRequest.newObjects, + lastModificationDate = changePropertyLabelsOrCommentsRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings = settings, + storeManager = storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. To make this comparison, // we have to undo the SPARQL-escaping of the input. - loadedPropertyDef <- loadPropertyDefinition( - propertyIri = internalPropertyIri, - featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig - ) - - unescapedNewLabelOrCommentPredicate: PredicateInfoV2 = PredicateInfoV2( - predicateIri = changePropertyLabelsOrCommentsRequest.predicateToUpdate, - objects = changePropertyLabelsOrCommentsRequest.newObjects - ).unescape - - unescapedNewPropertyDef: PropertyInfoContentV2 = currentReadPropertyInfo.entityInfoContent.copy( - predicates = currentReadPropertyInfo.entityInfoContent.predicates + (changePropertyLabelsOrCommentsRequest.predicateToUpdate -> unescapedNewLabelOrCommentPredicate) - ) + loadedPropertyDef <- OntologyHelpers.loadPropertyDefinition( + settings, + storeManager, + propertyIri = internalPropertyIri, + featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig + ) + + unescapedNewLabelOrCommentPredicate: PredicateInfoV2 = + PredicateInfoV2( + predicateIri = changePropertyLabelsOrCommentsRequest.predicateToUpdate, + objects = changePropertyLabelsOrCommentsRequest.newObjects + ).unescape + + unescapedNewPropertyDef: PropertyInfoContentV2 = + currentReadPropertyInfo.entityInfoContent.copy( + predicates = + currentReadPropertyInfo.entityInfoContent.predicates + (changePropertyLabelsOrCommentsRequest.predicateToUpdate -> unescapedNewLabelOrCommentPredicate) + ) _ = if (loadedPropertyDef != unescapedNewPropertyDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save property definition $unescapedNewPropertyDef, but $loadedPropertyDef was saved") - } + throw InconsistentRepositoryDataException( + s"Attempted to save property definition $unescapedNewPropertyDef, but $loadedPropertyDef was saved" + ) + } - maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = maybeCurrentLinkValueReadPropertyInfo - .map { linkValueReadPropertyInfo => - loadPropertyDefinition( + maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = + maybeCurrentLinkValueReadPropertyInfo.map { linkValueReadPropertyInfo => + OntologyHelpers.loadPropertyDefinition( + settings, + storeManager, propertyIri = linkValueReadPropertyInfo.entityInfoContent.propertyIri, featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig ) } - maybeLoadedLinkValuePropertyDef: Option[PropertyInfoContentV2] <- ActorUtil.optionFuture2FutureOption( - maybeLoadedLinkValuePropertyDefFuture) + maybeLoadedLinkValuePropertyDef: Option[PropertyInfoContentV2] <- + ActorUtil.optionFuture2FutureOption(maybeLoadedLinkValuePropertyDefFuture) - maybeUnescapedNewLinkValuePropertyDef: Option[PropertyInfoContentV2] = maybeLoadedLinkValuePropertyDef.map { - loadedLinkValuePropertyDef => + maybeUnescapedNewLinkValuePropertyDef: Option[PropertyInfoContentV2] = + maybeLoadedLinkValuePropertyDef.map { loadedLinkValuePropertyDef => val unescapedNewLinkPropertyDef = maybeCurrentLinkValueReadPropertyInfo.get.entityInfoContent.copy( - predicates = maybeCurrentLinkValueReadPropertyInfo.get.entityInfoContent.predicates + (changePropertyLabelsOrCommentsRequest.predicateToUpdate -> unescapedNewLabelOrCommentPredicate) + predicates = + maybeCurrentLinkValueReadPropertyInfo.get.entityInfoContent.predicates + (changePropertyLabelsOrCommentsRequest.predicateToUpdate -> unescapedNewLabelOrCommentPredicate) ) if (loadedLinkValuePropertyDef != unescapedNewLinkPropertyDef) { throw InconsistentRepositoryDataException( - s"Attempted to save link value property definition $unescapedNewLinkPropertyDef, but $loadedLinkValuePropertyDef was saved") + s"Attempted to save link value property definition $unescapedNewLinkPropertyDef, but $loadedLinkValuePropertyDef was saved" + ) } unescapedNewLinkPropertyDef - } + } // Update the ontology cache, using the unescaped definition(s). newReadPropertyInfo = ReadPropertyInfoV2( - entityInfoContent = unescapedNewPropertyDef, - isEditable = true, - isResourceProp = true, - isLinkProp = currentReadPropertyInfo.isLinkProp - ) - - maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = maybeUnescapedNewLinkValuePropertyDef - .map { unescapedNewLinkPropertyDef => + entityInfoContent = unescapedNewPropertyDef, + isEditable = true, + isResourceProp = true, + isLinkProp = currentReadPropertyInfo.isLinkProp + ) + + maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = + maybeUnescapedNewLinkValuePropertyDef.map { unescapedNewLinkPropertyDef => unescapedNewLinkPropertyDef.propertyIri -> ReadPropertyInfoV2( entityInfoContent = unescapedNewLinkPropertyDef, isResourceProp = true, @@ -4517,25 +3084,29 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } updatedOntologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ) - - updatedOntology = ontology.copy( - ontologyMetadata = updatedOntologyMetadata, - properties = ontology.properties ++ maybeLinkValuePropertyCacheEntry + (internalPropertyIri -> newReadPropertyInfo) - ) + lastModificationDate = Some(currentTime) + ) + + updatedOntology = + ontology.copy( + ontologyMetadata = updatedOntologyMetadata, + properties = + ontology.properties ++ maybeLinkValuePropertyCacheEntry + (internalPropertyIri -> newReadPropertyInfo) + ) - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - )) + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) // Read the data back from the cache. - response <- getPropertyDefinitionsFromOntologyV2(propertyIris = Set(internalPropertyIri), - allLanguages = true, - requestingUser = - changePropertyLabelsOrCommentsRequest.requestingUser) + response <- getPropertyDefinitionsFromOntologyV2( + propertyIris = Set(internalPropertyIri), + allLanguages = true, + requestingUser = changePropertyLabelsOrCommentsRequest.requestingUser + ) } yield response } @@ -4545,1027 +3116,168 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon externalPropertyIri = changePropertyLabelsOrCommentsRequest.propertyIri externalOntologyIri = externalPropertyIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalPropertyIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalPropertyIri, + requestingUser = requestingUser + ) internalPropertyIri = externalPropertyIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changePropertyLabelsOrCommentsRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalPropertyIri = internalPropertyIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changePropertyLabelsOrCommentsRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalPropertyIri = internalPropertyIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } /** - * Changes the values of `rdfs:label` or `rdfs:comment` in a class definition. - * - * @param changeClassLabelsOrCommentsRequest the request to change the class's labels or comments. - * @return a [[ReadOntologyV2]] containing the modified class definition. - */ + * Changes the values of `rdfs:label` or `rdfs:comment` in a class definition. + * + * @param changeClassLabelsOrCommentsRequest the request to change the class's labels or comments. + * @return a [[ReadOntologyV2]] containing the modified class definition. + */ private def changeClassLabelsOrComments( - changeClassLabelsOrCommentsRequest: ChangeClassLabelsOrCommentsRequestV2): Future[ReadOntologyV2] = { - def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { + changeClassLabelsOrCommentsRequest: ChangeClassLabelsOrCommentsRequestV2 + ): Future[ReadOntologyV2] = { + def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = for { - cacheData <- getCacheData + cacheData <- Cache.getCacheData ontology = cacheData.ontologies(internalOntologyIri) - currentReadClassInfo: ReadClassInfoV2 = ontology.classes.getOrElse( - internalClassIri, - throw NotFoundException(s"Class ${changeClassLabelsOrCommentsRequest.classIri} not found")) + currentReadClassInfo: ReadClassInfoV2 = + ontology.classes.getOrElse( + internalClassIri, + throw NotFoundException(s"Class ${changeClassLabelsOrCommentsRequest.classIri} not found") + ) // Check that the ontology exists and has not been updated by another user since the client last read it. - _ <- checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changeClassLabelsOrCommentsRequest.lastModificationDate, - featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changeClassLabelsOrCommentsRequest.lastModificationDate, + featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig + ) // Do the update. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changeClassLabelsOrComments( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - predicateToUpdate = changeClassLabelsOrCommentsRequest.predicateToUpdate, - newObjects = changeClassLabelsOrCommentsRequest.newObjects, - lastModificationDate = changeClassLabelsOrCommentsRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changeClassLabelsOrComments( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + predicateToUpdate = changeClassLabelsOrCommentsRequest.predicateToUpdate, + newObjects = changeClassLabelsOrCommentsRequest.newObjects, + lastModificationDate = changeClassLabelsOrCommentsRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. - _ <- checkOntologyLastModificationDateAfterUpdate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig - ) + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. To make this comparison, // we have to undo the SPARQL-escaping of the input. - loadedClassDef: ClassInfoContentV2 <- loadClassDefinition( - classIri = internalClassIri, - featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig - ) + loadedClassDef: ClassInfoContentV2 <- OntologyHelpers.loadClassDefinition( + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = + changeClassLabelsOrCommentsRequest.featureFactoryConfig + ) unescapedNewLabelOrCommentPredicate = PredicateInfoV2( - predicateIri = changeClassLabelsOrCommentsRequest.predicateToUpdate, - objects = changeClassLabelsOrCommentsRequest.newObjects - ).unescape - - unescapedNewClassDef: ClassInfoContentV2 = currentReadClassInfo.entityInfoContent.copy( - predicates = currentReadClassInfo.entityInfoContent.predicates + (changeClassLabelsOrCommentsRequest.predicateToUpdate -> unescapedNewLabelOrCommentPredicate) - ) + predicateIri = changeClassLabelsOrCommentsRequest.predicateToUpdate, + objects = changeClassLabelsOrCommentsRequest.newObjects + ).unescape + + unescapedNewClassDef: ClassInfoContentV2 = + currentReadClassInfo.entityInfoContent.copy( + predicates = + currentReadClassInfo.entityInfoContent.predicates + (changeClassLabelsOrCommentsRequest.predicateToUpdate -> unescapedNewLabelOrCommentPredicate) + ) _ = if (loadedClassDef != unescapedNewClassDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $unescapedNewClassDef, but $loadedClassDef was saved") - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $unescapedNewClassDef, but $loadedClassDef was saved" + ) + } // Update the ontology cache, using the unescaped definition(s). newReadClassInfo = currentReadClassInfo.copy( - entityInfoContent = unescapedNewClassDef - ) + entityInfoContent = unescapedNewClassDef + ) updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> newReadClassInfo) - ) - - _ = storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - )) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> newReadClassInfo) + ) + + _ = Cache.storeCacheData( + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = changeClassLabelsOrCommentsRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = changeClassLabelsOrCommentsRequest.requestingUser + ) } yield response - } for { requestingUser <- FastFuture.successful(changeClassLabelsOrCommentsRequest.requestingUser) - externalClassIri = changeClassLabelsOrCommentsRequest.classIri + externalClassIri = changeClassLabelsOrCommentsRequest.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity - _ <- checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changeClassLabelsOrCommentsRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changeClassLabelsOrCommentsRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } - /** - * Before an update of an ontology entity, checks that the entity's external IRI, and that of its ontology, - * are valid, and checks that the user has permission to update the ontology. - * - * @param externalOntologyIri the external IRI of the ontology. - * @param externalEntityIri the external IRI of the entity. - * @param requestingUser the user making the request. - */ - private def checkOntologyAndEntityIrisForUpdate(externalOntologyIri: SmartIri, - externalEntityIri: SmartIri, - requestingUser: UserADM): Future[Unit] = { - for { - _ <- checkExternalOntologyIriForUpdate(externalOntologyIri) - _ <- checkExternalEntityIriForUpdate(externalEntityIri = externalEntityIri) - _ <- checkPermissionsForOntologyUpdate( - internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema), - requestingUser = requestingUser - ) - } yield () - } - - /** - * Loads a property definition from the triplestore and converts it to a [[PropertyInfoContentV2]]. - * - * @param propertyIri the IRI of the property to be loaded. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[PropertyInfoContentV2]] representing the property definition. - */ - private def loadPropertyDefinition(propertyIri: SmartIri, - featureFactoryConfig: FeatureFactoryConfig): Future[PropertyInfoContentV2] = { - for { - sparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getPropertyDefinition( - triplestore = settings.triplestoreType, - propertyIri = propertyIri - ) - .toString()) - - constructResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = sparql, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse] - } yield - constructResponseToPropertyDefinition( - propertyIri = propertyIri, - constructResponse = constructResponse - ) - } - - /** - * Given a map of predicate IRIs to predicate objects describing an entity, returns a map of smart IRIs to [[PredicateInfoV2]] - * objects that can be used to construct an [[EntityInfoContentV2]]. - * - * @param entityDefMap a map of predicate IRIs to predicate objects. - * @return a map of smart IRIs to [[PredicateInfoV2]] objects. - */ - private def getEntityPredicatesFromConstructResponse( - entityDefMap: Map[SmartIri, Seq[LiteralV2]]): Map[SmartIri, PredicateInfoV2] = { - entityDefMap.map { - case (predicateIri: SmartIri, predObjs: Seq[LiteralV2]) => - val predicateInfo = PredicateInfoV2( - predicateIri = predicateIri, - objects = predObjs.map { - case IriLiteralV2(iriStr) => - // We use xsd:dateTime in the triplestore (because it is supported in SPARQL), but we return - // the more restrictive xsd:dateTimeStamp in the API. - if (iriStr == OntologyConstants.Xsd.DateTime) { - SmartIriLiteralV2(OntologyConstants.Xsd.DateTimeStamp.toSmartIri) - } else { - SmartIriLiteralV2(iriStr.toSmartIri) - } - - case ontoLiteral: OntologyLiteralV2 => ontoLiteral - - case other => - throw InconsistentRepositoryDataException(s"Predicate $predicateIri has an invalid object: $other") - } - ) - - predicateIri -> predicateInfo - } - } - - /** - * Extracts property definitions from a SPARQL CONSTRUCT response. - * - * @param propertyIris the IRIs of the properties to be read. - * @param constructResponse the SPARQL construct response to be read. - * @return a map of property IRIs to property definitions. - */ - private def constructResponseToPropertyDefinitions( - propertyIris: Set[SmartIri], - constructResponse: SparqlExtendedConstructResponse): Map[SmartIri, PropertyInfoContentV2] = { - propertyIris.map { propertyIri => - propertyIri -> constructResponseToPropertyDefinition( - propertyIri = propertyIri, - constructResponse = constructResponse - ) - }.toMap - } - - /** - * Converts a SPARQL CONSTRUCT response to a [[PropertyInfoContentV2]]. - * - * @param propertyIri the IRI of the property to be read. - * @param constructResponse the SPARQL CONSTRUCT response to be read. - * @return a [[PropertyInfoContentV2]] representing a property definition. - */ - private def constructResponseToPropertyDefinition( - propertyIri: SmartIri, - constructResponse: SparqlExtendedConstructResponse): PropertyInfoContentV2 = { - // All properties defined in the triplestore must be in Knora ontologies. - - val ontologyIri = propertyIri.getOntologyFromEntity - - if (!ontologyIri.isKnoraOntologyIri) { - throw InconsistentRepositoryDataException(s"Property $propertyIri is not in a Knora ontology") - } - - val statements = constructResponse.statements - - // Get the statements whose subject is the property. - val propertyDefMap: Map[SmartIri, Seq[LiteralV2]] = statements(IriSubjectV2(propertyIri.toString)) - - val subPropertyOf: Set[SmartIri] = propertyDefMap.get(OntologyConstants.Rdfs.SubPropertyOf.toSmartIri) match { - case Some(baseProperties) => - baseProperties.map { - case iriLiteral: IriLiteralV2 => iriLiteral.value.toSmartIri - case other => throw InconsistentRepositoryDataException(s"Unexpected object for rdfs:subPropertyOf: $other") - }.toSet - - case None => Set.empty[SmartIri] - } - - val otherPreds: Map[SmartIri, PredicateInfoV2] = getEntityPredicatesFromConstructResponse( - propertyDefMap - OntologyConstants.Rdfs.SubPropertyOf.toSmartIri) - - // salsah-gui:guiOrder isn't allowed here. - if (otherPreds.contains(OntologyConstants.SalsahGui.GuiOrder.toSmartIri)) { - throw InconsistentRepositoryDataException(s"Property $propertyIri contains salsah-gui:guiOrder") - } - - val propertyDef = PropertyInfoContentV2( - propertyIri = propertyIri, - subPropertyOf = subPropertyOf, - predicates = otherPreds, - ontologySchema = propertyIri.getOntologySchema.get - ) - - if (!propertyIri.isKnoraBuiltInDefinitionIri && propertyDef.getRdfTypes.contains( - OntologyConstants.Owl.TransitiveProperty.toSmartIri)) { - throw InconsistentRepositoryDataException( - s"Project-specific property $propertyIri cannot be an owl:TransitiveProperty") - } - - propertyDef - } - - /** - * Reads OWL named individuals from a SPARQL CONSTRUCT response. - * - * @param individualIris the IRIs of the named individuals to be read. - * @param constructResponse the SPARQL CONSTRUCT response. - * @return a map of individual IRIs to named individuals. - */ - private def constructResponseToIndividuals( - individualIris: Set[SmartIri], - constructResponse: SparqlExtendedConstructResponse): Map[SmartIri, IndividualInfoContentV2] = { - individualIris.map { individualIri => - individualIri -> constructResponseToIndividual( - individualIri = individualIri, - constructResponse = constructResponse - ) - }.toMap - } - - /** - * Reads an OWL named individual from a SPARQL CONSTRUCT response. - * - * @param individualIri the IRI of the individual to be read. - * @param constructResponse the SPARQL CONSTRUCT response. - * @return an [[IndividualInfoContentV2]] representing the named individual. - */ - private def constructResponseToIndividual( - individualIri: SmartIri, - constructResponse: SparqlExtendedConstructResponse): IndividualInfoContentV2 = { - val statements = constructResponse.statements - - // Get the statements whose subject is the individual. - val individualMap: Map[SmartIri, Seq[LiteralV2]] = statements(IriSubjectV2(individualIri.toString)) - - val predicates: Map[SmartIri, PredicateInfoV2] = getEntityPredicatesFromConstructResponse(individualMap) - - IndividualInfoContentV2( - individualIri = individualIri, - predicates = predicates, - ontologySchema = individualIri.getOntologySchema.get - ) - } - - /** - * Loads a class definition from the triplestore and converts it to a [[ClassInfoContentV2]]. - * - * @param classIri the IRI of the class to be loaded. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[ClassInfoContentV2]] representing the class definition. - */ - private def loadClassDefinition(classIri: SmartIri, - featureFactoryConfig: FeatureFactoryConfig): Future[ClassInfoContentV2] = { - for { - sparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getClassDefinition( - triplestore = settings.triplestoreType, - classIri = classIri - ) - .toString()) - - constructResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = sparql, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse] - } yield - constructResponseToClassDefinition( - classIri = classIri, - constructResponse = constructResponse - ) - } - - /** - * Extracts class definitions from a SPARQL CONSTRUCT response. - * - * @param classIris the IRIs of the classes to be read. - * @param constructResponse the SPARQL CONSTRUCT response to be read. - * @return a map of class IRIs to class definitions. - */ - private def constructResponseToClassDefinitions( - classIris: Set[SmartIri], - constructResponse: SparqlExtendedConstructResponse): Map[SmartIri, ClassInfoContentV2] = { - classIris.map { classIri => - classIri -> constructResponseToClassDefinition( - classIri = classIri, - constructResponse = constructResponse - ) - }.toMap - } - - /** - * Converts a SPARQL CONSTRUCT response to a [[ClassInfoContentV2]]. - * - * @param classIri the IRI of the class to be read. - * @param constructResponse the SPARQL CONSTRUCT response to be read. - * @return a [[ClassInfoContentV2]] representing a class definition. - */ - private def constructResponseToClassDefinition( - classIri: SmartIri, - constructResponse: SparqlExtendedConstructResponse): ClassInfoContentV2 = { - // All classes defined in the triplestore must be in Knora ontologies. - - val ontologyIri = classIri.getOntologyFromEntity - - if (!ontologyIri.isKnoraOntologyIri) { - throw InconsistentRepositoryDataException(s"Class $classIri is not in a Knora ontology") - } - - val statements = constructResponse.statements - - // Get the statements whose subject is the class. - val classDefMap: Map[SmartIri, Seq[LiteralV2]] = statements(IriSubjectV2(classIri.toString)) - - // Get the IRIs of the class's base classes. - - val subClassOfObjects: Seq[LiteralV2] = - classDefMap.getOrElse(OntologyConstants.Rdfs.SubClassOf.toSmartIri, Seq.empty[LiteralV2]) - - val subClassOf: Set[SmartIri] = subClassOfObjects.collect { - case iriLiteral: IriLiteralV2 => iriLiteral.value.toSmartIri - }.toSet - - // Get the blank nodes representing cardinalities. - - val restrictionBlankNodeIDs: Set[BlankNodeLiteralV2] = subClassOfObjects.collect { - case blankNodeLiteral: BlankNodeLiteralV2 => blankNodeLiteral - }.toSet - - val directCardinalities: Map[SmartIri, KnoraCardinalityInfo] = restrictionBlankNodeIDs.map { blankNodeID => - val blankNode: Map[SmartIri, Seq[LiteralV2]] = statements.getOrElse( - BlankNodeSubjectV2(blankNodeID.value), - throw InconsistentRepositoryDataException( - s"Blank node '${blankNodeID.value}' not found in construct query result") - ) - - val blankNodeTypeObjs: Seq[LiteralV2] = blankNode.getOrElse( - OntologyConstants.Rdf.Type.toSmartIri, - throw InconsistentRepositoryDataException(s"Blank node '${blankNodeID.value}' has no rdf:type")) - - blankNodeTypeObjs match { - case Seq(IriLiteralV2(OntologyConstants.Owl.Restriction)) => () - case _ => - throw InconsistentRepositoryDataException(s"Blank node '${blankNodeID.value}' is not an owl:Restriction") - } - - val onPropertyObjs: Seq[LiteralV2] = blankNode.getOrElse( - OntologyConstants.Owl.OnProperty.toSmartIri, - throw InconsistentRepositoryDataException(s"Blank node '${blankNodeID.value}' has no owl:onProperty")) - - val propertyIri: SmartIri = onPropertyObjs match { - case Seq(propertyIri: IriLiteralV2) => propertyIri.value.toSmartIri - case other => throw InconsistentRepositoryDataException(s"Invalid object for owl:onProperty: $other") - } - - val owlCardinalityPreds: Set[SmartIri] = blankNode.keySet.filter { predicate => - OntologyConstants.Owl.cardinalityOWLRestrictions(predicate.toString) - } - - if (owlCardinalityPreds.size != 1) { - throw InconsistentRepositoryDataException( - s"Expected one cardinality predicate in blank node '${blankNodeID.value}', got ${owlCardinalityPreds.size}") - } - - val owlCardinalityIri = owlCardinalityPreds.head - - val owlCardinalityValue: Int = blankNode(owlCardinalityIri) match { - case Seq(IntLiteralV2(intVal)) => intVal - case other => - throw InconsistentRepositoryDataException( - s"Expected one integer object for predicate $owlCardinalityIri in blank node '${blankNodeID.value}', got $other") - } - - val guiOrder: Option[Int] = blankNode.get(OntologyConstants.SalsahGui.GuiOrder.toSmartIri) match { - case Some(Seq(IntLiteralV2(intVal))) => Some(intVal) - case None => None - case other => - throw InconsistentRepositoryDataException( - s"Expected one integer object for predicate ${OntologyConstants.SalsahGui.GuiOrder} in blank node '${blankNodeID.value}', got $other") - } - - // salsah-gui:guiElement and salsah-gui:guiAttribute aren't allowed here. - - if (blankNode.contains(OntologyConstants.SalsahGui.GuiElementProp.toSmartIri)) { - throw InconsistentRepositoryDataException( - s"Class $classIri contains salsah-gui:guiElement in an owl:Restriction") - } - - if (blankNode.contains(OntologyConstants.SalsahGui.GuiAttribute.toSmartIri)) { - throw InconsistentRepositoryDataException( - s"Class $classIri contains salsah-gui:guiAttribute in an owl:Restriction") - } - - propertyIri -> Cardinality.owlCardinality2KnoraCardinality( - propertyIri = propertyIri.toString, - owlCardinality = Cardinality.OwlCardinalityInfo( - owlCardinalityIri = owlCardinalityIri.toString, - owlCardinalityValue = owlCardinalityValue, - guiOrder = guiOrder - ) - ) - }.toMap - - // Get any other predicates of the class. - - val otherPreds: Map[SmartIri, PredicateInfoV2] = getEntityPredicatesFromConstructResponse( - classDefMap - OntologyConstants.Rdfs.SubClassOf.toSmartIri) - - ClassInfoContentV2( - classIri = classIri, - subClassOf = subClassOf, - predicates = otherPreds, - directCardinalities = directCardinalities, - ontologySchema = classIri.getOntologySchema.get - ) - } - - /** - * Checks that a property's `knora-base:subjectClassConstraint` or `knora-base:objectClassConstraint` is compatible with (i.e. a subclass of) - * the ones in all its base properties. - * - * @param internalPropertyIri the internal IRI of the property to be checked. - * @param constraintPredicateIri the internal IRI of the constraint, i.e. `knora-base:subjectClassConstraint` or `knora-base:objectClassConstraint`. - * @param constraintValueToBeChecked the constraint value to be checked. - * @param allSuperPropertyIris the IRIs of all the base properties of the property, including indirect base properties and the property itself. - * @param errorSchema the ontology schema to be used for error messages. - * @param errorFun a function that throws an exception. It will be called with an error message argument if the property constraint is invalid. - */ - private def checkPropertyConstraint(cacheData: OntologyCacheData, - internalPropertyIri: SmartIri, - constraintPredicateIri: SmartIri, - constraintValueToBeChecked: SmartIri, - allSuperPropertyIris: Set[SmartIri], - errorSchema: OntologySchema, - errorFun: String => Nothing): Unit = { - // The property constraint value must be a Knora class, or one of a limited set of classes defined in OWL. - val superClassesOfConstraintValueToBeChecked: Set[SmartIri] = - if (OntologyConstants.Owl.ClassesThatCanBeKnoraClassConstraints.contains(constraintValueToBeChecked.toString)) { - Set(constraintValueToBeChecked) - } else { - cacheData.subClassOfRelations - .getOrElse( - constraintValueToBeChecked, - errorFun( - s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} cannot have a ${constraintPredicateIri - .toOntologySchema(errorSchema)} of " + - s"${constraintValueToBeChecked.toOntologySchema(errorSchema)}") - ) - .toSet - } - - // Get the definitions of all the Knora superproperties of the property. - val superPropertyInfos: Set[ReadPropertyInfoV2] = (allSuperPropertyIris - internalPropertyIri).collect { - case superPropertyIri if superPropertyIri.isKnoraDefinitionIri => - cacheData - .ontologies(superPropertyIri.getOntologyFromEntity) - .properties - .getOrElse( - superPropertyIri, - errorFun( - s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} is a subproperty of $superPropertyIri, which is undefined") - ) - } - - // For each superproperty definition, get the value of the specified constraint in that definition, if any. Here we - // make a map of superproperty IRIs to superproperty constraint values. - val superPropertyConstraintValues: Map[SmartIri, SmartIri] = superPropertyInfos.flatMap { superPropertyInfo => - superPropertyInfo.entityInfoContent.predicates - .get(constraintPredicateIri) - .map(_.requireIriObject(throw InconsistentRepositoryDataException( - s"Property ${superPropertyInfo.entityInfoContent.propertyIri} has an invalid object for $constraintPredicateIri"))) - .map { superPropertyConstraintValue => - superPropertyInfo.entityInfoContent.propertyIri -> superPropertyConstraintValue - } - }.toMap - - // Check that the constraint value to be checked is a subclass of the constraint value in every superproperty. - - superPropertyConstraintValues.foreach { - case (superPropertyIri, superPropertyConstraintValue) => - if (!superClassesOfConstraintValueToBeChecked.contains(superPropertyConstraintValue)) { - errorFun( - s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} cannot have a ${constraintPredicateIri - .toOntologySchema(errorSchema)} of " + - s"${constraintValueToBeChecked.toOntologySchema(errorSchema)}, because that is not a subclass of " + - s"${superPropertyConstraintValue.toOntologySchema(errorSchema)}, which is the ${constraintPredicateIri - .toOntologySchema(errorSchema)} of " + - s"a base property, ${superPropertyIri.toOntologySchema(errorSchema)}") - } - } - } - - /** - * Checks the last modification date of an ontology before an update. - * - * @param internalOntologyIri the internal IRI of the ontology. - * @param expectedLastModificationDate the last modification date that should now be attached to the ontology. - * @param featureFactoryConfig the feature factory configuration. - * @return a failed Future if the expected last modification date is not found. - */ - private def checkOntologyLastModificationDateBeforeUpdate( - internalOntologyIri: SmartIri, - expectedLastModificationDate: Instant, - featureFactoryConfig: FeatureFactoryConfig): Future[Unit] = { - checkOntologyLastModificationDate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = expectedLastModificationDate, - featureFactoryConfig = featureFactoryConfig, - errorFun = throw EditConflictException( - s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} has been modified by another user, please reload it and try again.") - ) - } - - /** - * Checks the last modification date of an ontology after an update. - * - * @param internalOntologyIri the internal IRI of the ontology. - * @param expectedLastModificationDate the last modification date that should now be attached to the ontology. - * @param featureFactoryConfig the feature factory configuration. - * @return a failed Future if the expected last modification date is not found. - */ - private def checkOntologyLastModificationDateAfterUpdate(internalOntologyIri: SmartIri, - expectedLastModificationDate: Instant, - featureFactoryConfig: FeatureFactoryConfig): Future[Unit] = { - checkOntologyLastModificationDate( - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = expectedLastModificationDate, - featureFactoryConfig = featureFactoryConfig, - errorFun = throw UpdateNotPerformedException( - s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} was not updated. Please report this as a possible bug.") - ) - } - - /** - * Checks the last modification date of an ontology. - * - * @param internalOntologyIri the internal IRI of the ontology. - * @param expectedLastModificationDate the last modification date that the ontology is expected to have. - * @param featureFactoryConfig the feature factory configuration. - * @param errorFun a function that throws an exception. It will be called if the expected last modification date is not found. - * @return a failed Future if the expected last modification date is not found. - */ - private def checkOntologyLastModificationDate(internalOntologyIri: SmartIri, - expectedLastModificationDate: Instant, - featureFactoryConfig: FeatureFactoryConfig, - errorFun: => Nothing): Future[Unit] = { - for { - existingOntologyMetadata: Option[OntologyMetadataV2] <- loadOntologyMetadata( - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = featureFactoryConfig - ) - - _ = existingOntologyMetadata match { - case Some(metadata) => - metadata.lastModificationDate match { - case Some(lastModificationDate) => - if (lastModificationDate != expectedLastModificationDate) { - errorFun - } - - case None => - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has no ${OntologyConstants.KnoraBase.LastModificationDate}") - } - - case None => - throw NotFoundException( - s"Ontology $internalOntologyIri (corresponding to ${internalOntologyIri.toOntologySchema(ApiV2Complex)}) not found") - } - } yield () - } - - /** - * Checks whether the requesting user has permission to update an ontology. - * - * @param internalOntologyIri the internal IRI of the ontology. - * @param requestingUser the user making the request. - * @return `true` if the user has permission to update the ontology - */ - private def canUserUpdateOntology(internalOntologyIri: SmartIri, requestingUser: UserADM): Future[Boolean] = { - for { - cacheData <- getCacheData - - projectIri = cacheData.ontologies - .getOrElse( - internalOntologyIri, - throw NotFoundException(s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} not found") - ) - .ontologyMetadata - .projectIri - .get - } yield requestingUser.permissions.isProjectAdmin(projectIri.toString) || requestingUser.permissions.isSystemAdmin - } - - /** - * Throws an exception if the requesting user does not have permission to update an ontology. - * - * @param internalOntologyIri the internal IRI of the ontology. - * @param requestingUser the user making the request. - * @return the project IRI. - */ - private def checkPermissionsForOntologyUpdate(internalOntologyIri: SmartIri, - requestingUser: UserADM): Future[SmartIri] = { - for { - cacheData <- getCacheData - - projectIri = cacheData.ontologies - .getOrElse( - internalOntologyIri, - throw NotFoundException(s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} not found") - ) - .ontologyMetadata - .projectIri - .get - - _ = if (!requestingUser.permissions.isProjectAdmin(projectIri.toString) && !requestingUser.permissions.isSystemAdmin) { - // not a project or system admin - throw ForbiddenException("Ontologies can be modified only by a project or system admin.") - } - - } yield projectIri - } - - /** - * Checks whether an ontology IRI is valid for an update. - * - * @param externalOntologyIri the external IRI of the ontology. - * @return a failed Future if the IRI is not valid for an update. - */ - private def checkExternalOntologyIriForUpdate(externalOntologyIri: SmartIri): Future[Unit] = { - if (!externalOntologyIri.isKnoraOntologyIri) { - FastFuture.failed(throw BadRequestException(s"Invalid ontology IRI for request: $externalOntologyIri}")) - } else if (!externalOntologyIri.getOntologySchema.contains(ApiV2Complex)) { - FastFuture.failed(throw BadRequestException(s"Invalid ontology schema for request: $externalOntologyIri")) - } else if (externalOntologyIri.isKnoraBuiltInDefinitionIri) { - FastFuture.failed( - throw BadRequestException(s"Ontology $externalOntologyIri cannot be modified via the Knora API")) - } else { - FastFuture.successful(()) - } - } - - /** - * Checks whether an entity IRI is valid for an update. - * - * @param externalEntityIri the external IRI of the entity. - * @return a failed Future if the entity IRI is not valid for an update, or is not from the specified ontology. - */ - private def checkExternalEntityIriForUpdate(externalEntityIri: SmartIri): Future[Unit] = { - if (!externalEntityIri.isKnoraApiV2EntityIri) { - FastFuture.failed(throw BadRequestException(s"Invalid entity IRI for request: $externalEntityIri")) - } else if (!externalEntityIri.getOntologySchema.contains(ApiV2Complex)) { - FastFuture.failed(throw BadRequestException(s"Invalid ontology schema for request: $externalEntityIri")) - } else { - FastFuture.successful(()) - } - } - - /** - * Given the definition of a link property, returns the definition of the corresponding link value property. - * - * @param internalPropertyDef the definition of the the link property, in the internal schema. - * @return the definition of the corresponding link value property. - */ - private def linkPropertyDefToLinkValuePropertyDef( - internalPropertyDef: PropertyInfoContentV2): PropertyInfoContentV2 = { - val linkValuePropIri = internalPropertyDef.propertyIri.fromLinkPropToLinkValueProp - - val newPredicates - : Map[SmartIri, PredicateInfoV2] = (internalPropertyDef.predicates - OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri) + - (OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri -> PredicateInfoV2( - predicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, - objects = Seq(SmartIriLiteralV2(OntologyConstants.KnoraBase.LinkValue.toSmartIri)) - )) - - internalPropertyDef.copy( - propertyIri = linkValuePropIri, - predicates = newPredicates, - subPropertyOf = Set(OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri) - ) - } - - /** - * Given the cardinalities directly defined on a given class, and the cardinalities that it could inherit (directly - * or indirectly) from its base classes, combines the two, filtering out the base class cardinalities ones that are overridden - * by cardinalities defined directly on the given class. Checks that if a directly defined cardinality overrides an inheritable one, - * the directly defined one is at least as restrictive as the inheritable one. - * - * @param classIri the class IRI. - * @param thisClassCardinalities the cardinalities directly defined on a given resource class. - * @param inheritableCardinalities the cardinalities that the given resource class could inherit from its base classes. - * @param allSubPropertyOfRelations a map in which each property IRI points to the full set of its base properties. - * @param errorSchema the ontology schema to be used in error messages. - * @param errorFun a function that throws an exception. It will be called with an error message argument if the cardinalities are invalid. - * @return a map in which each key is the IRI of a property that has a cardinality in the resource class (or that it inherits - * from its base classes), and each value is the cardinality on the property. - */ - private def overrideCardinalities(classIri: SmartIri, - thisClassCardinalities: Map[SmartIri, KnoraCardinalityInfo], - inheritableCardinalities: Map[SmartIri, KnoraCardinalityInfo], - allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], - errorSchema: OntologySchema, - errorFun: String => Nothing): Map[SmartIri, KnoraCardinalityInfo] = { - // A map of directly defined properties to the base class properties they can override. - val overrides: Map[SmartIri, Set[SmartIri]] = thisClassCardinalities.map { - case (thisClassProp, thisClassCardinality) => - // For each directly defined cardinality, get its base properties, if available. - // If the class has a cardinality for a non-Knora property like rdfs:label (which can happen only - // if it's a built-in class), we won't have any information about the base properties of that property. - val basePropsOfThisClassProp: Set[SmartIri] = - allSubPropertyOfRelations.getOrElse(thisClassProp, Set.empty[SmartIri]) - - val overriddenBaseProps: Set[SmartIri] = inheritableCardinalities.foldLeft(Set.empty[SmartIri]) { - case (acc, (baseClassProp, baseClassCardinality)) => - // Can the directly defined cardinality override the inheritable one? - if (thisClassProp == baseClassProp || basePropsOfThisClassProp.contains(baseClassProp)) { - // Yes. Is the directly defined one at least as restrictive as the inheritable one? - - if (!Cardinality.isCompatible(directCardinality = thisClassCardinality.cardinality, - inheritableCardinality = baseClassCardinality.cardinality)) { - // No. Throw an exception. - errorFun(s"In class <${classIri.toOntologySchema(errorSchema)}>, the directly defined cardinality ${thisClassCardinality.cardinality} on ${thisClassProp.toOntologySchema( - errorSchema)} is not compatible with the inherited cardinality ${baseClassCardinality.cardinality} on ${baseClassProp - .toOntologySchema(errorSchema)}, because it is less restrictive") - } else { - // Yes. Filter out the inheritable one, because the directly defined one overrides it. - acc + baseClassProp - } - } else { - // No. Let the class inherit the inheritable cardinality. - acc - } - } - - thisClassProp -> overriddenBaseProps - } - - // A map of base class properties to the directly defined properties that can override them. - val reverseOverrides: Map[SmartIri, Set[SmartIri]] = overrides.toVector - .flatMap { - // Unpack the sets to make an association list. - case (thisClassProp: SmartIri, baseClassProps: Set[SmartIri]) => - baseClassProps.map { baseClassProp: SmartIri => - thisClassProp -> baseClassProp - } - } - .map { - // Reverse the direction of the association list. - case (thisClassProp: SmartIri, baseClassProp: SmartIri) => - baseClassProp -> thisClassProp - } - .groupBy { - // Group by base class prop to make a map. - case (baseClassProp: SmartIri, _) => baseClassProp - } - .map { - // Make sets of directly defined props. - case (baseClassProp: SmartIri, thisClassProps: immutable.Iterable[(SmartIri, SmartIri)]) => - baseClassProp -> thisClassProps.map { - case (_, thisClassProp) => thisClassProp - }.toSet - } - - // Are there any base class properties that are overridden by more than one directly defined property, - // and do any of those base properties have cardinalities that could cause conflicts between the cardinalities - // on the directly defined cardinalities? - reverseOverrides.foreach { - case (baseClassProp, thisClassProps) => - if (thisClassProps.size > 1) { - val overriddenCardinality: KnoraCardinalityInfo = inheritableCardinalities(baseClassProp) - - if (overriddenCardinality.cardinality == Cardinality.MustHaveOne || overriddenCardinality.cardinality == Cardinality.MayHaveOne) { - errorFun( - s"In class <${classIri.toOntologySchema(errorSchema)}>, there is more than one cardinality that would override the inherited cardinality $overriddenCardinality on <${baseClassProp - .toOntologySchema(errorSchema)}>") - } - } - } - - thisClassCardinalities ++ inheritableCardinalities.filterNot { - case (basePropIri, _) => reverseOverrides.contains(basePropIri) - } - } - - /** - * Given all the `rdfs:subClassOf` relations between classes, calculates all the inverse relations. - * - * @param allSubClassOfRelations all the `rdfs:subClassOf` relations between classes. - * @return a map of IRIs of resource classes to sets of the IRIs of their subclasses. - */ - private def calculateSuperClassOfRelations( - allSubClassOfRelations: Map[SmartIri, Seq[SmartIri]]): Map[SmartIri, Set[SmartIri]] = { - allSubClassOfRelations.toVector - .flatMap { - case (subClass: SmartIri, baseClasses: Seq[SmartIri]) => - baseClasses.map { baseClass => - baseClass -> subClass - } - } - .groupBy(_._1) - .map { - case (baseClass: SmartIri, baseClassAndSubClasses: Vector[(SmartIri, SmartIri)]) => - baseClass -> baseClassAndSubClasses.map(_._2).toSet - } - } - - /** - * Given a class loaded from the triplestore, recursively adds its inherited cardinalities to the cardinalities it defines - * directly. A cardinality for a subproperty in a subclass overrides a cardinality for a base property in - * a base class. - * - * @param classIri the IRI of the class whose properties are to be computed. - * @param directSubClassOfRelations a map of the direct `rdfs:subClassOf` relations defined on each class. - * @param allSubPropertyOfRelations a map in which each property IRI points to the full set of its base properties. - * @param directClassCardinalities a map of the cardinalities defined directly on each class. - * @return a map in which each key is the IRI of a property that has a cardinality in the class (or that it inherits - * from its base classes), and each value is the cardinality on the property. - */ - private def inheritCardinalitiesInLoadedClass( - classIri: SmartIri, - directSubClassOfRelations: Map[SmartIri, Set[SmartIri]], - allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], - directClassCardinalities: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]]) - : Map[SmartIri, KnoraCardinalityInfo] = { - // Recursively get properties that are available to inherit from base classes. If we have no information about - // a class, that could mean that it isn't a subclass of knora-base:Resource (e.g. it's something like - // foaf:Person), in which case we assume that it has no base classes. - val cardinalitiesAvailableToInherit: Map[SmartIri, KnoraCardinalityInfo] = directSubClassOfRelations - .getOrElse(classIri, Set.empty[SmartIri]) - .foldLeft(Map.empty[SmartIri, KnoraCardinalityInfo]) { - case (acc: Map[SmartIri, KnoraCardinalityInfo], baseClass: SmartIri) => - acc ++ inheritCardinalitiesInLoadedClass( - classIri = baseClass, - directSubClassOfRelations = directSubClassOfRelations, - allSubPropertyOfRelations = allSubPropertyOfRelations, - directClassCardinalities = directClassCardinalities - ) - } - - // Get the properties that have cardinalities defined directly on this class. Again, if we have no information - // about a class, we assume that it has no cardinalities. - val thisClassCardinalities: Map[SmartIri, KnoraCardinalityInfo] = - directClassCardinalities.getOrElse(classIri, Map.empty[SmartIri, KnoraCardinalityInfo]) - - // Combine the cardinalities defined directly on this class with the ones that are available to inherit. - overrideCardinalities( - classIri = classIri, - thisClassCardinalities = thisClassCardinalities, - inheritableCardinalities = cardinalitiesAvailableToInherit, - allSubPropertyOfRelations = allSubPropertyOfRelations, - errorSchema = InternalSchema, { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) - } - - /** - * Checks whether a class IRI refers to a Knora internal resource class. - * - * @param classIri the class IRI. - * @return `true` if the class IRI refers to a Knora resource class, or `false` if the class - * does not exist or is not a Knora internal resource class. - */ - private def isKnoraInternalResourceClass(classIri: SmartIri, cacheData: OntologyCacheData): Boolean = { - classIri.isKnoraInternalEntityIri && - cacheData.ontologies(classIri.getOntologyFromEntity).classes.get(classIri).exists(_.isResourceClass) - } - - /** - * Checks whether a property is a subproperty of `knora-base:resourceProperty`. - * - * @param propertyIri the property IRI. - * @param cacheData the ontology cache. - * @return `true` if the property is a subproperty of `knora-base:resourceProperty`. - */ - private def isKnoraResourceProperty(propertyIri: SmartIri, cacheData: OntologyCacheData): Boolean = { - propertyIri.isKnoraEntityIri && - cacheData.ontologies(propertyIri.getOntologyFromEntity).properties.get(propertyIri).exists(_.isResourceProp) - } - - /** - * Checks whether a property is a subproperty of `knora-base:hasLinkTo`. - * - * @param propertyIri the property IRI. - * @param cacheData the ontology cache. - * @return `true` if the property is a subproperty of `knora-base:hasLinkTo`. - */ - private def isLinkProp(propertyIri: SmartIri, cacheData: OntologyCacheData): Boolean = { - propertyIri.isKnoraEntityIri && - cacheData.ontologies(propertyIri.getOntologyFromEntity).properties.get(propertyIri).exists(_.isLinkProp) - } - - /** - * Checks whether a property is a subproperty of `knora-base:hasLinkToValue`. - * - * @param propertyIri the property IRI. - * @param cacheData the ontology cache. - * @return `true` if the property is a subproperty of `knora-base:hasLinkToValue`. - */ - private def isLinkValueProp(propertyIri: SmartIri, cacheData: OntologyCacheData): Boolean = { - propertyIri.isKnoraEntityIri && - cacheData.ontologies(propertyIri.getOntologyFromEntity).properties.get(propertyIri).exists(_.isLinkValueProp) - } - - /** - * Checks whether a property is a subproperty of `knora-base:hasFileValue`. - * - * @param propertyIri the property IRI. - * @param cacheData the ontology cache. - * @return `true` if the property is a subproperty of `knora-base:hasFileValue`. - */ - private def isFileValueProp(propertyIri: SmartIri, cacheData: OntologyCacheData): Boolean = { - propertyIri.isKnoraEntityIri && - cacheData.ontologies(propertyIri.getOntologyFromEntity).properties.get(propertyIri).exists(_.isFileValueProp) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/BUILD.bazel b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/BUILD.bazel new file mode 100644 index 0000000000..459bc2c432 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/BUILD.bazel @@ -0,0 +1,27 @@ +package(default_visibility = ["//visibility:public"]) + +load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library") + +scala_library( + name = "ontology", + srcs = [ + "Cache.scala", + "Cardinalities.scala", + "OntologyHelpers.scala", + ], + scalacopts = ["-deprecation"], + unused_dependency_checker_mode = "warn", + deps = [ + "//webapi/src/main/scala/org/knora/webapi", + "//webapi/src/main/scala/org/knora/webapi/exceptions", + "//webapi/src/main/scala/org/knora/webapi/feature", + "//webapi/src/main/scala/org/knora/webapi/messages", + "//webapi/src/main/scala/org/knora/webapi/settings", + "//webapi/src/main/scala/org/knora/webapi/util/cache", + "@maven//:com_typesafe_akka_akka_actor_2_13", + "@maven//:com_typesafe_akka_akka_http_core_2_13", + "@maven//:com_typesafe_play_twirl_api_2_13", + "@maven//:com_typesafe_scala_logging_scala_logging_2_13", + "@maven//:org_slf4j_slf4j_api", + ], +) diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cache.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cache.scala new file mode 100644 index 0000000000..0195d5aaff --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cache.scala @@ -0,0 +1,795 @@ +/* + * Copyright © 2015-2021 the contributors (see Contributors.md). + * + * This file is part of the DaSCH Service Platform. + * + * The DaSCH Service Platform 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. + * + * The DaSCH Service Platform 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 the DaSCH Service Platform. If not, see + * . + */ + +package org.knora.webapi.responders.v2.ontology + +import akka.actor.ActorRef +import akka.http.scaladsl.util.FastFuture +import org.knora.webapi.{ApiV2Complex, InternalSchema, KnoraBaseVersion, OntologySchema} +import org.knora.webapi.exceptions.{ + ApplicationCacheException, + BadRequestException, + ForbiddenException, + InconsistentRepositoryDataException +} +import org.knora.webapi.feature.FeatureFactoryConfig +import org.knora.webapi.util.cache.CacheUtil +import org.knora.webapi.messages.IriConversions._ +import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} +import org.knora.webapi.messages.StringFormatter.SalsahGuiAttributeDefinition +import org.knora.webapi.messages.admin.responder.usersmessages.UserADM +import org.knora.webapi.messages.store.triplestoremessages.{ + SparqlExtendedConstructRequest, + SparqlExtendedConstructResponse, + SparqlSelectRequest +} +import org.knora.webapi.messages.util.rdf.SparqlSelectResult +import org.knora.webapi.messages.util.{ErrorHandlingMap, KnoraSystemInstances, OntologyUtil} +import org.knora.webapi.messages.v2.responder.SuccessResponseV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.KnoraCardinalityInfo +import org.knora.webapi.messages.v2.responder.ontologymessages.{ + ClassInfoContentV2, + IndividualInfoContentV2, + OntologyMetadataV2, + PropertyInfoContentV2, + ReadClassInfoV2, + ReadOntologyV2, + ReadPropertyInfoV2 +} +import org.knora.webapi.responders.v2.ontology.OntologyHelpers.OntologyGraph +import org.knora.webapi.settings.KnoraSettingsImpl + +import scala.concurrent.{ExecutionContext, Future} +import akka.pattern._ +import akka.util.Timeout +import com.typesafe.scalalogging.{LazyLogging, Logger} + +object Cache extends LazyLogging { + + private val log: Logger = logger + + // The name of the ontology cache. + private val OntologyCacheName = "ontologyCache" + + // The cache key under which cached ontology data is stored. + private val OntologyCacheKey = "ontologyCacheData" + + // The global ontology cache lock. This is needed because every ontology update replaces the whole ontology cache + // (because definitions in one ontology can refer to definitions in another ontology). Without a global lock, + // concurrent updates (even to different ontologies) could overwrite each other. + val ONTOLOGY_CACHE_LOCK_IRI = "http://rdfh.ch/ontologies" + + /** + * The in-memory cache of ontologies. + * + * @param ontologies a map of ontology IRIs to ontologies. + * @param subClassOfRelations a map of subclasses to their base classes. + * @param superClassOfRelations a map of base classes to their subclasses. + * @param subPropertyOfRelations a map of subproperties to their base proeprties. + * @param guiAttributeDefinitions a map of salsah-gui:Guielement individuals to their GUI attribute definitions. + * @param standoffProperties a set of standoff properties. + */ + case class OntologyCacheData(ontologies: Map[SmartIri, ReadOntologyV2], + subClassOfRelations: Map[SmartIri, Seq[SmartIri]], + superClassOfRelations: Map[SmartIri, Set[SmartIri]], + subPropertyOfRelations: Map[SmartIri, Set[SmartIri]], + guiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]], + standoffProperties: Set[SmartIri]) { + lazy val allPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = ontologies.values + .flatMap(_.properties.map { + case (propertyIri, readPropertyInfo) => propertyIri -> readPropertyInfo.entityInfoContent + }) + .toMap + } + + /** + * Loads and caches all ontology information. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[SuccessResponseV2]]. + */ + def loadOntologies(settings: KnoraSettingsImpl, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM)(implicit ec: ExecutionContext, + stringFormat: StringFormatter, + timeout: Timeout): Future[SuccessResponseV2] = { + val loadOntologiesFuture: Future[SuccessResponseV2] = for { + _ <- Future { + if (!(requestingUser.id == KnoraSystemInstances.Users.SystemUser.id || requestingUser.permissions.isSystemAdmin)) { + throw ForbiddenException(s"Only a system administrator can reload ontologies") + } + } + + // Get all ontology metadata. + allOntologyMetadataSparql <- FastFuture.successful( + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .getAllOntologyMetadata(triplestore = settings.triplestoreType) + .toString()) + allOntologyMetadataResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(allOntologyMetadataSparql)) + .mapTo[SparqlSelectResult] + allOntologyMetadata: Map[SmartIri, OntologyMetadataV2] = OntologyHelpers.buildOntologyMetadata( + allOntologyMetadataResponse) + + knoraBaseOntologyMetadata: OntologyMetadataV2 = allOntologyMetadata.getOrElse( + OntologyConstants.KnoraBase.KnoraBaseOntologyIri.toSmartIri, + throw InconsistentRepositoryDataException(s"No knora-base ontology found")) + knoraBaseOntologyVersion: String = knoraBaseOntologyMetadata.ontologyVersion.getOrElse( + throw InconsistentRepositoryDataException( + "The knora-base ontology in the repository is not up to date. See the Knora documentation on repository updates.")) + + _ = if (knoraBaseOntologyVersion != KnoraBaseVersion) { + throw InconsistentRepositoryDataException( + s"The knora-base ontology in the repository has version '$knoraBaseOntologyVersion', but this version of Knora requires '$KnoraBaseVersion'. See the Knora documentation on repository updates.") + } + + // Get the contents of each named graph containing an ontology. + ontologyGraphResponseFutures: Iterable[Future[OntologyGraph]] = allOntologyMetadata.keys.map { ontologyIri => + val ontologyGraphConstructQuery = org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .getOntologyGraph( + triplestore = settings.triplestoreType, + ontologyGraph = ontologyIri + ) + .toString + + (storeManager ? SparqlExtendedConstructRequest( + sparql = ontologyGraphConstructQuery, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse].map { response => + OntologyGraph(ontologyIri = ontologyIri, constructResponse = response) + } + } + + ontologyGraphs: Iterable[OntologyGraph] <- Future.sequence(ontologyGraphResponseFutures) + + _ = makeOntologyCache(allOntologyMetadata, ontologyGraphs) + } yield SuccessResponseV2("Ontologies loaded.") + + loadOntologiesFuture.recover { + case exception: Throwable => + exception match { + case inconsistentRepositoryDataException: InconsistentRepositoryDataException => + log.error(inconsistentRepositoryDataException.message) + SuccessResponseV2( + s"An error occurred when loading ontologies: ${inconsistentRepositoryDataException.message}") + + case other => throw other + } + } + } + + /** + * Given ontology metdata and ontology graphs read from the triplestore, constructs the ontology cache. + * + * @param allOntologyMetadata a map of ontology IRIs to ontology metadata. + * @param ontologyGraphs a list of ontology graphs. + */ + def makeOntologyCache(allOntologyMetadata: Map[SmartIri, OntologyMetadataV2], + ontologyGraphs: Iterable[OntologyGraph])(implicit stringFormatter: StringFormatter): Unit = { + // Get the IRIs of all the entities in each ontology. + + // A map of ontology IRIs to class IRIs in each ontology. + val classIrisPerOntology: Map[SmartIri, Set[SmartIri]] = OntologyHelpers.getEntityIrisFromOntologyGraphs( + ontologyGraphs = ontologyGraphs, + entityTypes = Set(OntologyConstants.Owl.Class) + ) + + // A map of ontology IRIs to property IRIs in each ontology. + val propertyIrisPerOntology: Map[SmartIri, Set[SmartIri]] = OntologyHelpers.getEntityIrisFromOntologyGraphs( + ontologyGraphs = ontologyGraphs, + entityTypes = Set( + OntologyConstants.Owl.ObjectProperty, + OntologyConstants.Owl.DatatypeProperty, + OntologyConstants.Owl.AnnotationProperty, + OntologyConstants.Rdf.Property + ) + ) + + // A map of ontology IRIs to named individual IRIs in each ontology. + val individualIrisPerOntology: Map[SmartIri, Set[SmartIri]] = OntologyHelpers.getEntityIrisFromOntologyGraphs( + ontologyGraphs = ontologyGraphs, + entityTypes = Set(OntologyConstants.Owl.NamedIndividual) + ) + + // Construct entity definitions. + + // A map of class IRIs to class definitions. + val allClassDefs: Map[SmartIri, ClassInfoContentV2] = ontologyGraphs.flatMap { ontologyGraph => + OntologyHelpers.constructResponseToClassDefinitions( + classIris = classIrisPerOntology(ontologyGraph.ontologyIri), + constructResponse = ontologyGraph.constructResponse + ) + }.toMap + + // A map of property IRIs to property definitions. + val allPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = ontologyGraphs.flatMap { ontologyGraph => + OntologyHelpers.constructResponseToPropertyDefinitions( + propertyIris = propertyIrisPerOntology(ontologyGraph.ontologyIri), + constructResponse = ontologyGraph.constructResponse + ) + }.toMap + + // A map of OWL named individual IRIs to named individuals. + val allIndividuals: Map[SmartIri, IndividualInfoContentV2] = ontologyGraphs.flatMap { ontologyGraph => + OntologyHelpers.constructResponseToIndividuals( + individualIris = individualIrisPerOntology(ontologyGraph.ontologyIri), + constructResponse = ontologyGraph.constructResponse + ) + }.toMap + + // A map of salsah-gui:Guielement individuals to their GUI attribute definitions. + val allGuiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]] = + OntologyHelpers.makeGuiAttributeDefinitions(allIndividuals) + + // Determine relations between entities. + + // A map of class IRIs to their immediate base classes. + val directSubClassOfRelations: Map[SmartIri, Set[SmartIri]] = allClassDefs.map { + case (classIri, classDef) => classIri -> classDef.subClassOf + } + + // A map of property IRIs to their immediate base properties. + val directSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]] = allPropertyDefs.map { + case (propertyIri, propertyDef) => propertyIri -> propertyDef.subPropertyOf + } + + val allClassIris = allClassDefs.keySet + val allPropertyIris = allPropertyDefs.keySet + + // A map in which each class IRI points to the full sequence of its base classes. + val allSubClassOfRelations: Map[SmartIri, Seq[SmartIri]] = allClassIris.toSeq.map { classIri => + // get the hierarchically ordered base classes. + val baseClasses: Seq[SmartIri] = OntologyUtil.getAllBaseDefs(classIri, directSubClassOfRelations) + // prepend the classIri to the sequence of base classes because a class is also a subclass of itself. + (classIri, classIri +: baseClasses) + }.toMap + + // A map in which each class IRI points to the full set of its subclasses. A class is also + // a subclass of itself. + val allSuperClassOfRelations: Map[SmartIri, Set[SmartIri]] = + OntologyHelpers.calculateSuperClassOfRelations(allSubClassOfRelations) + + // Make a map in which each property IRI points to the full set of its base properties. A property is also + // a subproperty of itself. + val allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]] = allPropertyIris.map { propertyIri => + (propertyIri, OntologyUtil.getAllBaseDefs(propertyIri, directSubPropertyOfRelations).toSet + propertyIri) + }.toMap + + // A set of all subproperties of knora-base:resourceProperty. + val allKnoraResourceProps: Set[SmartIri] = allPropertyIris.filter { prop => + val allPropSubPropertyOfRelations = allSubPropertyOfRelations(prop) + prop == OntologyConstants.KnoraBase.ResourceProperty.toSmartIri || + allPropSubPropertyOfRelations.contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) || + allPropSubPropertyOfRelations.contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri) + } + + // A set of all subproperties of knora-base:hasLinkTo. + val allLinkProps: Set[SmartIri] = allPropertyIris.filter(prop => + allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri)) + + // A set of all subproperties of knora-base:hasLinkToValue. + val allLinkValueProps: Set[SmartIri] = allPropertyIris.filter(prop => + allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri)) + + // A set of all subproperties of knora-base:hasFileValue. + val allFileValueProps: Set[SmartIri] = allPropertyIris.filter(prop => + allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasFileValue.toSmartIri)) + + // A map of the cardinalities defined directly on each resource class. Each class IRI points to a map of + // property IRIs to KnoraCardinalityInfo objects. + val directClassCardinalities: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] = allClassDefs.map { + case (classIri, classDef) => + classIri -> classDef.directCardinalities + } + + // Allow each class to inherit cardinalities from its base classes. + val classCardinalitiesWithInheritance: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] = allClassIris.map { + resourceClassIri => + val resourceClassCardinalities: Map[SmartIri, KnoraCardinalityInfo] = + OntologyHelpers.inheritCardinalitiesInLoadedClass( + classIri = resourceClassIri, + directSubClassOfRelations = directSubClassOfRelations, + allSubPropertyOfRelations = allSubPropertyOfRelations, + directClassCardinalities = directClassCardinalities + ) + + resourceClassIri -> resourceClassCardinalities + }.toMap + + // Construct a ReadClassInfoV2 for each class. + val readClassInfos: Map[SmartIri, ReadClassInfoV2] = OntologyHelpers.makeReadClassInfos( + classDefs = allClassDefs, + directClassCardinalities = directClassCardinalities, + classCardinalitiesWithInheritance = classCardinalitiesWithInheritance, + directSubClassOfRelations = directSubClassOfRelations, + allSubClassOfRelations = allSubClassOfRelations, + allSubPropertyOfRelations = allSubPropertyOfRelations, + allPropertyDefs = allPropertyDefs, + allKnoraResourceProps = allKnoraResourceProps, + allLinkProps = allLinkProps, + allLinkValueProps = allLinkValueProps, + allFileValueProps = allFileValueProps + ) + + // Construct a ReadPropertyInfoV2 for each property definition. + val readPropertyInfos: Map[SmartIri, ReadPropertyInfoV2] = OntologyHelpers.makeReadPropertyInfos( + propertyDefs = allPropertyDefs, + directSubPropertyOfRelations = directSubPropertyOfRelations, + allSubPropertyOfRelations = allSubPropertyOfRelations, + allSubClassOfRelations = allSubClassOfRelations, + allGuiAttributeDefinitions = allGuiAttributeDefinitions, + allKnoraResourceProps = allKnoraResourceProps, + allLinkProps = allLinkProps, + allLinkValueProps = allLinkValueProps, + allFileValueProps = allFileValueProps + ) + + // Construct a ReadIndividualV2 for each OWL named individual. + val readIndividualInfos = OntologyHelpers.makeReadIndividualInfos(allIndividuals) + + // A ReadOntologyV2 for each ontology to be cached. + val readOntologies: Map[SmartIri, ReadOntologyV2] = allOntologyMetadata.map { + case (ontologyIri, ontologyMetadata) => + ontologyIri -> ReadOntologyV2( + ontologyMetadata = ontologyMetadata, + classes = readClassInfos.filter { + case (classIri, _) => classIri.getOntologyFromEntity == ontologyIri + }, + properties = readPropertyInfos.filter { + case (propertyIri, _) => propertyIri.getOntologyFromEntity == ontologyIri + }, + individuals = readIndividualInfos.filter { + case (individualIri, _) => individualIri.getOntologyFromEntity == ontologyIri + }, + isWholeOntology = true + ) + } + + // A set of the IRIs of all properties used in cardinalities in standoff classes. + val propertiesUsedInStandoffCardinalities: Set[SmartIri] = readClassInfos.flatMap { + case (_, readClassInfo) => + if (readClassInfo.isStandoffClass) { + readClassInfo.allCardinalities.keySet + } else { + Set.empty[SmartIri] + } + }.toSet + + // A set of the IRIs of all properties whose subject class constraint is a standoff class. + val propertiesWithStandoffTagSubjects: Set[SmartIri] = readPropertyInfos.flatMap { + case (propertyIri, readPropertyInfo) => + readPropertyInfo.entityInfoContent.getPredicateIriObject( + OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { + case Some(subjectClassConstraint: SmartIri) => + readClassInfos.get(subjectClassConstraint) match { + case Some(subjectReadClassInfo: ReadClassInfoV2) => + if (subjectReadClassInfo.isStandoffClass) { + Some(propertyIri) + } else { + None + } + + case None => None + } + + case None => None + } + }.toSet + + // Construct the ontology cache data. + val ontologyCacheData: OntologyCacheData = OntologyCacheData( + ontologies = new ErrorHandlingMap[SmartIri, ReadOntologyV2](readOntologies, { key => + s"Ontology not found: $key" + }), + subClassOfRelations = new ErrorHandlingMap[SmartIri, Seq[SmartIri]](allSubClassOfRelations, { key => + s"Class not found: $key" + }), + superClassOfRelations = new ErrorHandlingMap[SmartIri, Set[SmartIri]](allSuperClassOfRelations, { key => + s"Class not found: $key" + }), + subPropertyOfRelations = new ErrorHandlingMap[SmartIri, Set[SmartIri]](allSubPropertyOfRelations, { key => + s"Property not found: $key" + }), + guiAttributeDefinitions = + new ErrorHandlingMap[SmartIri, Set[SalsahGuiAttributeDefinition]](allGuiAttributeDefinitions, { key => + s"salsah-gui:Guielement not found: $key" + }), + standoffProperties = propertiesUsedInStandoffCardinalities ++ propertiesWithStandoffTagSubjects + ) + + // Check property subject and object class constraints. + + readPropertyInfos.foreach { + case (propertyIri, readPropertyInfo) => + val allSuperPropertyIris: Set[SmartIri] = allSubPropertyOfRelations.getOrElse(propertyIri, Set.empty[SmartIri]) + + readPropertyInfo.entityInfoContent.getPredicateIriObject( + OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { + case Some(subjectClassConstraint) => + // Each property's subject class constraint, if provided, must be a subclass of the subject class constraints of all its base properties. + checkPropertyConstraint( + cacheData = ontologyCacheData, + internalPropertyIri = propertyIri, + constraintPredicateIri = OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri, + constraintValueToBeChecked = subjectClassConstraint, + allSuperPropertyIris = allSuperPropertyIris, + errorSchema = InternalSchema, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) + + // If the property is defined in a project-specific ontology, its subject class constraint, if provided, must be a Knora resource or standoff class. + if (!propertyIri.isKnoraBuiltInDefinitionIri) { + val baseClassesOfSubjectClassConstraint = allSubClassOfRelations(subjectClassConstraint) + + if (!(baseClassesOfSubjectClassConstraint.contains(OntologyConstants.KnoraBase.Resource.toSmartIri) || + baseClassesOfSubjectClassConstraint.contains(OntologyConstants.KnoraBase.StandoffTag.toSmartIri))) { + throw InconsistentRepositoryDataException( + s"Property $propertyIri is defined in a project-specific ontology, but its knora-base:subjectClassConstraint, $subjectClassConstraint, is not a subclass of knora-base:Resource or knora-base:StandoffTag") + } + } + + case None => () + } + + readPropertyInfo.entityInfoContent.getPredicateIriObject( + OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri) match { + case Some(objectClassConstraint) => + // Each property's object class constraint, if provided, must be a subclass of the object class constraints of all its base properties. + checkPropertyConstraint( + cacheData = ontologyCacheData, + internalPropertyIri = propertyIri, + constraintPredicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, + constraintValueToBeChecked = objectClassConstraint, + allSuperPropertyIris = allSuperPropertyIris, + errorSchema = InternalSchema, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) + + case None => + // A resource property must have an object class constraint, unless it's knora-base:resourceProperty. + if (readPropertyInfo.isResourceProp && propertyIri != OntologyConstants.KnoraBase.ResourceProperty.toSmartIri) { + throw InconsistentRepositoryDataException( + s"Property $propertyIri has no knora-base:objectClassConstraint") + } + } + } + + // Check references between ontologies. + checkReferencesBetweenOntologies(ontologyCacheData) + + // Update the cache. + storeCacheData(ontologyCacheData) + } + + /** + * Checks that a property's `knora-base:subjectClassConstraint` or `knora-base:objectClassConstraint` is compatible with (i.e. a subclass of) + * the ones in all its base properties. + * + * @param internalPropertyIri the internal IRI of the property to be checked. + * @param constraintPredicateIri the internal IRI of the constraint, i.e. `knora-base:subjectClassConstraint` or `knora-base:objectClassConstraint`. + * @param constraintValueToBeChecked the constraint value to be checked. + * @param allSuperPropertyIris the IRIs of all the base properties of the property, including indirect base properties and the property itself. + * @param errorSchema the ontology schema to be used for error messages. + * @param errorFun a function that throws an exception. It will be called with an error message argument if the property constraint is invalid. + */ + def checkPropertyConstraint(cacheData: OntologyCacheData, + internalPropertyIri: SmartIri, + constraintPredicateIri: SmartIri, + constraintValueToBeChecked: SmartIri, + allSuperPropertyIris: Set[SmartIri], + errorSchema: OntologySchema, + errorFun: String => Nothing): Unit = { + // The property constraint value must be a Knora class, or one of a limited set of classes defined in OWL. + val superClassesOfConstraintValueToBeChecked: Set[SmartIri] = + if (OntologyConstants.Owl.ClassesThatCanBeKnoraClassConstraints.contains(constraintValueToBeChecked.toString)) { + Set(constraintValueToBeChecked) + } else { + cacheData.subClassOfRelations + .getOrElse( + constraintValueToBeChecked, + errorFun( + s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} cannot have a ${constraintPredicateIri + .toOntologySchema(errorSchema)} of " + + s"${constraintValueToBeChecked.toOntologySchema(errorSchema)}") + ) + .toSet + } + + // Get the definitions of all the Knora superproperties of the property. + val superPropertyInfos: Set[ReadPropertyInfoV2] = (allSuperPropertyIris - internalPropertyIri).collect { + case superPropertyIri if superPropertyIri.isKnoraDefinitionIri => + cacheData + .ontologies(superPropertyIri.getOntologyFromEntity) + .properties + .getOrElse( + superPropertyIri, + errorFun( + s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} is a subproperty of $superPropertyIri, which is undefined") + ) + } + + // For each superproperty definition, get the value of the specified constraint in that definition, if any. Here we + // make a map of superproperty IRIs to superproperty constraint values. + val superPropertyConstraintValues: Map[SmartIri, SmartIri] = superPropertyInfos.flatMap { superPropertyInfo => + superPropertyInfo.entityInfoContent.predicates + .get(constraintPredicateIri) + .map(_.requireIriObject(throw InconsistentRepositoryDataException( + s"Property ${superPropertyInfo.entityInfoContent.propertyIri} has an invalid object for $constraintPredicateIri"))) + .map { superPropertyConstraintValue => + superPropertyInfo.entityInfoContent.propertyIri -> superPropertyConstraintValue + } + }.toMap + + // Check that the constraint value to be checked is a subclass of the constraint value in every superproperty. + + superPropertyConstraintValues.foreach { + case (superPropertyIri, superPropertyConstraintValue) => + if (!superClassesOfConstraintValueToBeChecked.contains(superPropertyConstraintValue)) { + errorFun( + s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} cannot have a ${constraintPredicateIri + .toOntologySchema(errorSchema)} of " + + s"${constraintValueToBeChecked.toOntologySchema(errorSchema)}, because that is not a subclass of " + + s"${superPropertyConstraintValue.toOntologySchema(errorSchema)}, which is the ${constraintPredicateIri + .toOntologySchema(errorSchema)} of " + + s"a base property, ${superPropertyIri.toOntologySchema(errorSchema)}") + } + } + } + + /** + * Checks a reference between an ontology entity and another ontology entity to see if the target + * is in a non-shared ontology in another project. + * + * @param ontologyCacheData the ontology cache data. + * @param sourceEntityIri the entity whose definition contains the reference. + * @param targetEntityIri the entity that's the target of the reference. + * @param errorFun a function that throws an exception with the specified message if the reference is invalid. + */ + private def checkOntologyReferenceInEntity(ontologyCacheData: OntologyCacheData, + sourceEntityIri: SmartIri, + targetEntityIri: SmartIri, + errorFun: String => Nothing): Unit = { + if (targetEntityIri.isKnoraDefinitionIri) { + val sourceOntologyIri = sourceEntityIri.getOntologyFromEntity + val sourceOntologyMetadata = ontologyCacheData.ontologies(sourceOntologyIri).ontologyMetadata + + val targetOntologyIri = targetEntityIri.getOntologyFromEntity + val targetOntologyMetadata = ontologyCacheData.ontologies(targetOntologyIri).ontologyMetadata + + if (sourceOntologyMetadata.projectIri != targetOntologyMetadata.projectIri) { + if (!(targetOntologyIri.isKnoraBuiltInDefinitionIri || targetOntologyIri.isKnoraSharedDefinitionIri)) { + errorFun( + s"Entity $sourceEntityIri refers to entity $targetEntityIri, which is in a non-shared ontology that belongs to another project") + } + } + } + } + + /** + * Checks a property definition to ensure that it doesn't refer to any other non-shared ontologies. + * + * @param ontologyCacheData the ontology cache data. + * @param propertyDef the property definition. + * @param errorFun a function that throws an exception with the specified message if the property definition is invalid. + */ + def checkOntologyReferencesInPropertyDef( + ontologyCacheData: OntologyCacheData, + propertyDef: PropertyInfoContentV2, + errorFun: String => Nothing)(implicit stringFormatter: StringFormatter): Unit = { + // Ensure that the property isn't a subproperty of any property in a non-shared ontology in another project. + + for (subPropertyOf <- propertyDef.subPropertyOf) { + checkOntologyReferenceInEntity( + ontologyCacheData = ontologyCacheData, + sourceEntityIri = propertyDef.propertyIri, + targetEntityIri = subPropertyOf, + errorFun = errorFun + ) + } + + // Ensure that the property doesn't have subject or object constraints pointing to a non-shared ontology in another project. + + propertyDef.getPredicateIriObject(OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { + case Some(subjectClassConstraint) => + checkOntologyReferenceInEntity( + ontologyCacheData = ontologyCacheData, + sourceEntityIri = propertyDef.propertyIri, + targetEntityIri = subjectClassConstraint, + errorFun = errorFun + ) + + case None => () + } + + propertyDef.getPredicateIriObject(OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri) match { + case Some(objectClassConstraint) => + checkOntologyReferenceInEntity( + ontologyCacheData = ontologyCacheData, + sourceEntityIri = propertyDef.propertyIri, + targetEntityIri = objectClassConstraint, + errorFun = errorFun + ) + + case None => () + } + } + + /** + * Checks a class definition to ensure that it doesn't refer to any non-shared ontologies in other projects. + * + * @param ontologyCacheData the ontology cache data. + * @param classDef the class definition. + * @param errorFun a function that throws an exception with the specified message if the property definition is invalid. + */ + def checkOntologyReferencesInClassDef(ontologyCacheData: OntologyCacheData, + classDef: ClassInfoContentV2, + errorFun: String => Nothing): Unit = { + for (subClassOf <- classDef.subClassOf) { + checkOntologyReferenceInEntity( + ontologyCacheData = ontologyCacheData, + sourceEntityIri = classDef.classIri, + targetEntityIri = subClassOf, + errorFun = errorFun + ) + } + + for (cardinalityPropIri <- classDef.directCardinalities.keys) { + checkOntologyReferenceInEntity( + ontologyCacheData = ontologyCacheData, + sourceEntityIri = classDef.classIri, + targetEntityIri = cardinalityPropIri, + errorFun = errorFun + ) + } + } + + /** + * Checks references between ontologies to ensure that they do not refer to non-shared ontologies in other projects. + * + * @param ontologyCacheData the ontology cache data. + */ + private def checkReferencesBetweenOntologies(ontologyCacheData: OntologyCacheData)( + implicit stringFormatter: StringFormatter): Unit = { + for (ontology <- ontologyCacheData.ontologies.values) { + for (propertyInfo <- ontology.properties.values) { + checkOntologyReferencesInPropertyDef( + ontologyCacheData = ontologyCacheData, + propertyDef = propertyInfo.entityInfoContent, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) + } + + for (classInfo <- ontology.classes.values) { + checkOntologyReferencesInClassDef( + ontologyCacheData = ontologyCacheData, + classDef = classInfo.entityInfoContent, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) + } + } + } + + /** + * Updates the ontology cache. + * + * @param cacheData the updated data to be cached. + */ + def storeCacheData(cacheData: OntologyCacheData): Unit = { + CacheUtil.put(cacheName = OntologyCacheName, key = OntologyCacheKey, value = cacheData) + } + + /** + * Gets the ontology data from the cache. + * + * @return an [[OntologyCacheData]] + */ + def getCacheData(implicit ec: ExecutionContext): Future[OntologyCacheData] = { + Future { + CacheUtil.get[OntologyCacheData](cacheName = OntologyCacheName, key = OntologyCacheKey) match { + case Some(data) => data + case None => + throw ApplicationCacheException( + s"The Knora API server has not loaded any ontologies, perhaps because of an invalid ontology") + } + } + } + + /** + * Given the IRI of a base class, updates inherited cardinalities in subclasses. + * + * @param baseClassIri the internal IRI of the base class. + * @param cacheData the ontology cache. + * + * @return the updated ontology cache. + */ + def updateSubClasses(baseClassIri: SmartIri, cacheData: OntologyCacheData): OntologyCacheData = { + // Get the class definitions of all the subclasses of the base class. + + val allSubClassIris: Set[SmartIri] = cacheData.superClassOfRelations(baseClassIri) + + val allSubClasses: Set[ReadClassInfoV2] = allSubClassIris.map { subClassIri => + cacheData.ontologies(subClassIri.getOntologyFromEntity).classes(subClassIri) + } + + // Filter them to get only the direct subclasses. + + val directSubClasses: Set[ReadClassInfoV2] = allSubClasses.filter { subClass => + subClass.entityInfoContent.subClassOf + .contains(baseClassIri) && subClass.entityInfoContent.classIri != baseClassIri + } + + // Iterate over the subclasses, updating cardinalities. + val cacheDataWithUpdatedSubClasses = directSubClasses.foldLeft(cacheData) { + case (cacheDataAcc: OntologyCacheData, directSubClass: ReadClassInfoV2) => + val directSubClassIri = directSubClass.entityInfoContent.classIri + + // Get the cardinalities that this subclass can inherit from its direct base classes. + + val inheritableCardinalities: Map[SmartIri, KnoraCardinalityInfo] = + directSubClass.entityInfoContent.subClassOf.flatMap { baseClassIri => + cacheData.ontologies(baseClassIri.getOntologyFromEntity).classes(baseClassIri).allCardinalities + }.toMap + + // Override inherited cardinalities with directly defined cardinalities. + val newInheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = OntologyHelpers.overrideCardinalities( + classIri = directSubClassIri, + thisClassCardinalities = directSubClass.entityInfoContent.directCardinalities, + inheritableCardinalities = inheritableCardinalities, + allSubPropertyOfRelations = cacheData.subPropertyOfRelations, + errorSchema = ApiV2Complex, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) + + // Update the cache. + + val ontologyIri = directSubClass.entityInfoContent.classIri.getOntologyFromEntity + val ontology: ReadOntologyV2 = cacheDataAcc.ontologies(ontologyIri) + + val updatedOntology = ontology.copy( + classes = ontology.classes + (directSubClassIri -> directSubClass.copy( + inheritedCardinalities = newInheritedCardinalities + )) + ) + + cacheDataAcc.copy( + ontologies = cacheDataAcc.ontologies + (ontologyIri -> updatedOntology) + ) + } + + // Recurse to subclasses of subclasses. + + directSubClasses.map(_.entityInfoContent.classIri).foldLeft(cacheDataWithUpdatedSubClasses) { + case (cacheDataAcc: OntologyCacheData, directSubClassIri: SmartIri) => + updateSubClasses(baseClassIri = directSubClassIri, cacheDataAcc) + } + } + +} diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cardinalities.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cardinalities.scala new file mode 100644 index 0000000000..a066401af1 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cardinalities.scala @@ -0,0 +1,518 @@ +/* + * Copyright © 2015-2021 the contributors (see Contributors.md). + * + * This file is part of the DaSCH Service Platform. + * + * The DaSCH Service Platform 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. + * + * The DaSCH Service Platform 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 the DaSCH Service Platform. If not, see + * . + */ + +package org.knora.webapi.responders.v2.ontology + +import akka.actor.ActorRef +import akka.http.scaladsl.util.FastFuture +import akka.pattern._ +import akka.util.Timeout +import org.knora.webapi.InternalSchema +import org.knora.webapi.exceptions.{BadRequestException, InconsistentRepositoryDataException} +import org.knora.webapi.messages.IriConversions._ +import org.knora.webapi.messages.OntologyConstants.KnoraBase +import org.knora.webapi.messages.store.triplestoremessages.{ + SparqlAskRequest, + SparqlAskResponse, + SparqlUpdateRequest, + SparqlUpdateResponse +} +import org.knora.webapi.messages.v2.responder.CanDoResponseV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.KnoraCardinalityInfo +import org.knora.webapi.messages.v2.responder.ontologymessages._ +import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} +import org.knora.webapi.settings.KnoraSettingsImpl + +import java.time.Instant +import scala.concurrent.{ExecutionContext, Future} + +/** + * Contains methods used for dealing with cardinalities on a class + */ +object Cardinalities { + + /** + * FIXME(DSP-1856): Only works if a single cardinality is supplied. + * + * @param settings the applications settings. + * @param storeManager the store manager actor. + * @param deleteCardinalitiesFromClassRequest the requested cardinalities to be deleted. + * @param internalClassIri the Class from which the cardinalities are deleted. + * @param internalOntologyIri the Ontology of which the Class and Cardinalities are part of. + * @return a [[CanDoResponseV2]] indicating whether a class's cardinalities can be deleted. + */ + def canDeleteCardinalitiesFromClass( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + deleteCardinalitiesFromClassRequest: CanDeleteCardinalitiesFromClassRequestV2, + internalClassIri: SmartIri, + internalOntologyIri: SmartIri + )(implicit ec: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[CanDoResponseV2] = { + for { + cacheData: Cache.OntologyCacheData <- Cache.getCacheData + ontology = cacheData.ontologies(internalOntologyIri) + + submittedClassDefinition: ClassInfoContentV2 = + deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema) + + // Check that the ontology exists and has not been updated by another user since the client last read it. + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, + featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig + ) + + // Check that the class's rdf:type is owl:Class. + + rdfType: SmartIri = submittedClassDefinition.requireIriObject( + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) + + _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } + + // Check that cardinalities were submitted. + + _ = if (submittedClassDefinition.directCardinalities.isEmpty) { + throw BadRequestException("No cardinalities specified") + } + + // Check that only one cardinality was submitted. + + _ = if (submittedClassDefinition.directCardinalities.size > 1) { + throw BadRequestException("Only one cardinality is allowed to be submitted.") + } + + // Check that the class exists + currentClassDefinition: ClassInfoContentV2 <- + classExists( + cacheData, + deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema), + internalClassIri, + internalOntologyIri + ) + + // FIXME: This seems not to work or is not needed. Check that it is a subclass of knora-base:Resource + // _ <- isKnoraResourceClass(deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema)) + + // Check that the submitted cardinality to delete is defined on this class + + cardinalitiesToDelete: Map[SmartIri, Cardinality.KnoraCardinalityInfo] = + deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema).directCardinalities + + _ = cardinalitiesToDelete.foreach(p => + isCardinalityDefinedOnClass(cacheData, p._1, p._2, internalClassIri, internalOntologyIri) + ) + + // Check if property is used in resources + + submittedPropertyToDelete: SmartIri = cardinalitiesToDelete.head._1 + propertyIsUsed: Boolean <- isPropertyUsedInResources(settings, storeManager, submittedPropertyToDelete) + _ = if (propertyIsUsed) { + throw BadRequestException("Property is used in data. The cardinality cannot be deleted.") + } + + // Make an update class definition in which the cardinality to delete is removed + + newClassDefinitionWithRemovedCardinality = + currentClassDefinition.copy( + directCardinalities = currentClassDefinition.directCardinalities - submittedPropertyToDelete + ) + + // FIXME: Refactor. From here on is copy-paste from `changeClassCardinalities`, which I don't fully understand + + // Check that the new cardinalities are valid, and don't add any inherited cardinalities. + + allBaseClassIrisWithoutInternal: Seq[SmartIri] = + newClassDefinitionWithRemovedCardinality.subClassOf.toSeq.flatMap { baseClassIri => + cacheData.subClassOfRelations.getOrElse( + baseClassIri, + Seq.empty[SmartIri] + ) + } + + allBaseClassIris: Seq[SmartIri] = internalClassIri +: allBaseClassIrisWithoutInternal + + (newInternalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = + OntologyHelpers + .checkCardinalitiesBeforeAdding( + internalClassDef = newClassDefinitionWithRemovedCardinality, + allBaseClassIris = allBaseClassIris.toSet, + cacheData = cacheData + ) + + // Check that the class definition doesn't refer to any non-shared ontologies in other projects. + _ = Cache.checkOntologyReferencesInClassDef( + ontologyCacheData = cacheData, + classDef = newInternalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) + } yield CanDoResponseV2(true) + } + + /** + * FIXME(DSP-1856): Only works if a single cardinality is supplied. + * Deletes the supplied cardinalities from a class, if the referenced properties are not used in instances + * of the class and any subclasses. + * + * @param settings the applications settings. + * @param storeManager the store manager actor. + * @param deleteCardinalitiesFromClassRequest the requested cardinalities to be deleted. + * @param internalClassIri the Class from which the cardinalities are deleted. + * @param internalOntologyIri the Ontology of which the Class and Cardinalities are part of. + * @return a [[ReadOntologyV2]] in the internal schema, containing the new class definition. + */ + def deleteCardinalitiesFromClass( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + deleteCardinalitiesFromClassRequest: DeleteCardinalitiesFromClassRequestV2, + internalClassIri: SmartIri, + internalOntologyIri: SmartIri + )(implicit ec: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[ReadOntologyV2] = + for { + cacheData: Cache.OntologyCacheData <- Cache.getCacheData + ontology = cacheData.ontologies(internalOntologyIri) + submittedClassDefinition: ClassInfoContentV2 = + deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema) + + // Check that the ontology exists and has not been updated by another user since the client last read it. + _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, + featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig + ) + + // Check that the class's rdf:type is owl:Class. + + rdfType: SmartIri = submittedClassDefinition.requireIriObject( + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) + + _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } + + // Check that cardinalities were submitted. + + _ = if (submittedClassDefinition.directCardinalities.isEmpty) { + throw BadRequestException("No cardinalities specified") + } + + // Check that only one cardinality was submitted. + + _ = if (submittedClassDefinition.directCardinalities.size > 1) { + throw BadRequestException("Only one cardinality is allowed to be submitted.") + } + + // Check that the class exists + currentClassDefinition: ClassInfoContentV2 <- + classExists( + cacheData, + deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema), + internalClassIri, + internalOntologyIri + ) + + // Check that the submitted cardinality to delete is defined on this class + + cardinalitiesToDelete: Map[SmartIri, Cardinality.KnoraCardinalityInfo] = + deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema).directCardinalities + + _ = cardinalitiesToDelete.foreach(p => + isCardinalityDefinedOnClass(cacheData, p._1, p._2, internalClassIri, internalOntologyIri) + ) + + // Check if property is used in resources + + submittedPropertyToDelete: SmartIri = cardinalitiesToDelete.head._1 + propertyIsUsed: Boolean <- isPropertyUsedInResources(settings, storeManager, submittedPropertyToDelete) + _ = if (propertyIsUsed) { + throw BadRequestException("Property is used in data. The cardinality cannot be deleted.") + } + + // Make an update class definition in which the cardinality to delete is removed + + newClassDefinitionWithRemovedCardinality = + currentClassDefinition.copy( + directCardinalities = currentClassDefinition.directCardinalities - submittedPropertyToDelete + ) + + // FIXME: Refactor. From here on is copy-paste from `changeClassCardinalities`, which I don't fully understand + + // Check that the new cardinalities are valid, and don't add any inherited cardinalities. + + allBaseClassIrisWithoutInternal: Seq[SmartIri] = + newClassDefinitionWithRemovedCardinality.subClassOf.toSeq.flatMap { baseClassIri => + cacheData.subClassOfRelations.getOrElse( + baseClassIri, + Seq.empty[SmartIri] + ) + } + + allBaseClassIris: Seq[SmartIri] = internalClassIri +: allBaseClassIrisWithoutInternal + + (newInternalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = + OntologyHelpers + .checkCardinalitiesBeforeAdding( + internalClassDef = newClassDefinitionWithRemovedCardinality, + allBaseClassIris = allBaseClassIris.toSet, + cacheData = cacheData + ) + + // Check that the class definition doesn't refer to any non-shared ontologies in other projects. + _ = Cache.checkOntologyReferencesInClassDef( + ontologyCacheData = cacheData, + classDef = newInternalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) + + // Prepare to update the ontology cache. (No need to deal with SPARQL-escaping here, because there + // isn't any text to escape in cardinalities.) + + propertyIrisOfAllCardinalitiesForClass = cardinalitiesForClassWithInheritance.keySet + + inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = + cardinalitiesForClassWithInheritance.filterNot { case (propertyIri, _) => + newInternalClassDefWithLinkValueProps.directCardinalities.contains(propertyIri) + } + + readClassInfo = ReadClassInfoV2( + entityInfoContent = newInternalClassDefWithLinkValueProps, + allBaseClasses = allBaseClassIris, + isResourceClass = true, + canBeInstantiated = true, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + ), + linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkProp(propertyIri, cacheData) + ), + linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkValueProp(propertyIri, cacheData) + ), + fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isFileValueProp(propertyIri, cacheData) + ) + ) + + // Add the cardinalities to the class definition in the triplestore. + + currentTime: Instant = Instant.now + + updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .replaceClassCardinalities( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + newCardinalities = newInternalClassDefWithLinkValueProps.directCardinalities, + lastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() + + _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] + + // Check that the ontology's last modification date was updated. + + _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig + ) + + // Check that the data that was saved corresponds to the data that was submitted. + + loadedClassDef <- OntologyHelpers.loadClassDefinition( + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig + ) + + _ = if (loadedClassDef != newInternalClassDefWithLinkValueProps) { + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" + ) + } + + // Update subclasses and write the cache. + + updatedOntology = ontology.copy( + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> readClassInfo) + ) + + _ = Cache.storeCacheData( + Cache.updateSubClasses( + baseClassIri = internalClassIri, + cacheData = cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) + ) + + // Read the data back from the cache. + + response: ReadOntologyV2 <- OntologyHelpers.getClassDefinitionsFromOntologyV2( + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = deleteCardinalitiesFromClassRequest.requestingUser + ) + + } yield response + + /** + * Check if a property entity is used in resource instances. Returns `true` if + * it is used, and `false` if it is not used. + * + * @param settings application settings. + * @param storeManager store manager actor ref. + * @param internalPropertyIri the IRI of the entity that is being checked for usage. + * @param ec the execution context onto with the future will run. + * @param timeout the timeout for the future. + * @return a [[Boolean]] denoting if the property entity is used. + */ + def isPropertyUsedInResources( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + internalPropertyIri: SmartIri + )(implicit ec: ExecutionContext, timeout: Timeout): Future[Boolean] = + for { + request <- Future( + org.knora.webapi.queries.sparql.v2.txt + .isPropertyUsed( + triplestore = settings.triplestoreType, + internalPropertyIri = internalPropertyIri.toString, + ignoreKnoraConstraints = true, + ignoreRdfSubjectAndObject = true + ) + .toString() + ) + response: SparqlAskResponse <- + (storeManager ? SparqlAskRequest(request)).mapTo[SparqlAskResponse] + } yield response.result + + /** + * Checks if the class is defined inside the ontology found in the cache. + * + * @param cacheData the cached ontology data + * @param submittedClassInfoContentV2 the submitted class information + * @param internalClassIri the internal class IRI + * @param internalOntologyIri the internal ontology IRI + * @return `true` if the class is defined inside the ontology found in the cache, otherwise throws an exception. + */ + def classExists( + cacheData: Cache.OntologyCacheData, + submittedClassInfoContentV2: ClassInfoContentV2, + internalClassIri: SmartIri, + internalOntologyIri: SmartIri + )(implicit ec: ExecutionContext): Future[ClassInfoContentV2] = for { + currentOntologyState: ReadOntologyV2 <- Future(cacheData.ontologies(internalOntologyIri)) + currentClassDefinition = currentOntologyState.classes + .getOrElse( + internalClassIri, + throw BadRequestException( + s"Class ${submittedClassInfoContentV2.classIri} does not exist" + ) + ) + .entityInfoContent + } yield currentClassDefinition + + /** + * Checks if the class is a subclass of `knora-base:Resource`. + * + * @param submittedClassInfoContentV2 the class to check + * @return `true` if the class is a subclass of `knora-base:Resource`, otherwise throws an exception. + */ + def isKnoraResourceClass( + submittedClassInfoContentV2: ClassInfoContentV2 + )(implicit ec: ExecutionContext, stringFormatter: StringFormatter): Future[Boolean] = + if (submittedClassInfoContentV2.subClassOf.contains(KnoraBase.Resource.toSmartIri)) { + FastFuture.successful(true) + } else { + FastFuture.failed( + throw BadRequestException( + s"Class ${submittedClassInfoContentV2.classIri} is not a subclass of ${KnoraBase.Resource.toSmartIri}. $submittedClassInfoContentV2" + ) + ) + } + + /** + * Check if the cardinality for a property is defined on a class. + * + * @param cacheData the cached ontology data. + * @param propertyIri the property IRI for which we want to check if the cardinality is defined on the class. + * @param cardinalityInfo the cardinality that should be defined for the property. + * @param internalClassIri the class we are checking against. + * @param internalOntologyIri the ontology containing the class. + * @return `true` if the cardinality is defined on the class, otherwise throws an exception. + */ + def isCardinalityDefinedOnClass( + cacheData: Cache.OntologyCacheData, + propertyIri: SmartIri, + cardinalityInfo: KnoraCardinalityInfo, + internalClassIri: SmartIri, + internalOntologyIri: SmartIri + )(implicit ec: ExecutionContext): Future[Boolean] = { + val currentOntologyState: ReadOntologyV2 = cacheData.ontologies(internalOntologyIri) + val currentClassState: ClassInfoContentV2 = currentOntologyState.classes + .getOrElse( + internalClassIri, + throw BadRequestException( + s"Class ${internalClassIri} does not exist" + ) + ) + .entityInfoContent + val existingCardinality = currentClassState.directCardinalities + .getOrElse( + propertyIri, + throw BadRequestException( + s"Cardinality for property ${propertyIri} is not defined." + ) + ) + if (existingCardinality.cardinality.equals(cardinalityInfo.cardinality)) { + FastFuture.successful(true) + } else { + FastFuture.failed( + throw BadRequestException( + s"Submitted cardinality for property ${propertyIri} does not match existing cardinality." + ) + ) + } + } + +} 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 new file mode 100644 index 0000000000..4774a01eba --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala @@ -0,0 +1,2208 @@ +/* + * Copyright © 2015-2021 the contributors (see Contributors.md). + * + * This file is part of the DaSCH Service Platform. + * + * The DaSCH Service Platform 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. + * + * The DaSCH Service Platform 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 the DaSCH Service Platform. If not, see + * . + */ + +package org.knora.webapi.responders.v2.ontology + +import akka.actor.ActorRef +import akka.http.scaladsl.util.FastFuture +import akka.pattern._ +import akka.util.Timeout +import org.knora.webapi.exceptions._ +import org.knora.webapi.feature.FeatureFactoryConfig +import org.knora.webapi.messages.IriConversions._ +import org.knora.webapi.messages.StringFormatter.{SalsahGuiAttribute, SalsahGuiAttributeDefinition} +import org.knora.webapi.messages.admin.responder.usersmessages.UserADM +import org.knora.webapi.messages.store.triplestoremessages._ +import org.knora.webapi.messages.util.ErrorHandlingMap +import org.knora.webapi.messages.util.rdf.{SparqlSelectResult, VariableResultsRow} +import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.KnoraCardinalityInfo +import org.knora.webapi.messages.v2.responder.ontologymessages._ +import org.knora.webapi.messages.v2.responder.standoffmessages.StandoffDataTypeClasses +import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} +import org.knora.webapi.responders.v2.ontology.Cache.OntologyCacheData +import org.knora.webapi.settings.KnoraSettingsImpl +import org.knora.webapi.{ApiV2Complex, ApiV2Schema, ApiV2Simple, IRI, InternalSchema, OntologySchema} + +import java.time.Instant +import scala.collection.immutable +import scala.concurrent.{ExecutionContext, Future} + +object OntologyHelpers { + + /** + * Represents the contents of a named graph representing an ontology. + * + * @param ontologyIri the ontology IRI, which is also the IRI of the named graph. + * @param constructResponse the triplestore's response to a CONSTRUCT query that gets the contents of the named graph. + */ + case class OntologyGraph(ontologyIri: SmartIri, constructResponse: SparqlExtendedConstructResponse) + + /** + * Given the triplestore's response to `getAllOntologyMetadata.scala.txt`, constructs a map of ontology IRIs + * to ontology metadata for the ontology cache. + * + * @param allOntologyMetadataResponse the triplestore's response to the SPARQL query `getAllOntologyMetadata.scala.txt`. + * @return a map of ontology IRIs to ontology metadata. + */ + def buildOntologyMetadata( + allOntologyMetadataResponse: SparqlSelectResult + )(implicit stringFormatter: StringFormatter): Map[SmartIri, OntologyMetadataV2] = + allOntologyMetadataResponse.results.bindings.groupBy(_.rowMap("ontologyGraph")).map { + case (ontologyGraph: IRI, rows: Seq[VariableResultsRow]) => + val ontologyIri = rows.head.rowMap("ontologyIri") + + if (ontologyIri != ontologyGraph) { + throw InconsistentRepositoryDataException( + s"Ontology $ontologyIri must be stored in named graph $ontologyIri, but it is in $ontologyGraph" + ) + } + + val ontologySmartIri = ontologyIri.toSmartIri + + if (!ontologySmartIri.isKnoraOntologyIri) { + throw InconsistentRepositoryDataException(s"Ontology $ontologySmartIri is not a Knora ontology") + } + + val ontologyMetadataMap: Map[IRI, String] = rows.map { row => + val pred = + row.rowMap.getOrElse( + "ontologyPred", + throw InconsistentRepositoryDataException(s"Empty predicate in ontology $ontologyIri") + ) + val obj = row.rowMap.getOrElse( + "ontologyObj", + throw InconsistentRepositoryDataException(s"Empty object for predicate $pred in ontology $ontologyIri") + ) + pred -> obj + }.toMap + + val projectIri: SmartIri = ontologyMetadataMap + .getOrElse( + OntologyConstants.KnoraBase.AttachedToProject, + throw InconsistentRepositoryDataException(s"Ontology $ontologyIri has no knora-base:attachedToProject") + ) + .toSmartIri + val ontologyLabel: String = + ontologyMetadataMap.getOrElse(OntologyConstants.Rdfs.Label, ontologySmartIri.getOntologyName) + val lastModificationDate: Option[Instant] = ontologyMetadataMap + .get(OntologyConstants.KnoraBase.LastModificationDate) + .map(instant => + stringFormatter.xsdDateTimeStampToInstant( + instant, + throw InconsistentRepositoryDataException(s"Invalid UTC instant: $instant") + ) + ) + val ontologyVersion: Option[String] = ontologyMetadataMap.get(OntologyConstants.KnoraBase.OntologyVersion) + + ontologySmartIri -> OntologyMetadataV2( + ontologyIri = ontologySmartIri, + projectIri = Some(projectIri), + label = Some(ontologyLabel), + lastModificationDate = lastModificationDate, + ontologyVersion = ontologyVersion + ) + } + + /** + * Reads an ontology's metadata. + * + * @param internalOntologyIri the ontology's internal IRI. + * @param featureFactoryConfig the feature factory configuration. + * @return an [[OntologyMetadataV2]], or [[None]] if the ontology is not found. + */ + def loadOntologyMetadata( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + internalOntologyIri: SmartIri, + featureFactoryConfig: FeatureFactoryConfig + )(implicit + executionContext: ExecutionContext, + stringFormatter: StringFormatter, + timeout: Timeout + ): Future[Option[OntologyMetadataV2]] = { + for { + _ <- Future { + if (!internalOntologyIri.getOntologySchema.contains(InternalSchema)) { + throw AssertionException(s"Expected an internal ontology IRI: $internalOntologyIri") + } + } + + getOntologyInfoSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .getOntologyInfo( + triplestore = settings.triplestoreType, + ontologyIri = internalOntologyIri + ) + .toString() + + getOntologyInfoResponse <- (storeManager ? SparqlConstructRequest( + sparql = getOntologyInfoSparql, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlConstructResponse] + + metadata: Option[OntologyMetadataV2] = if (getOntologyInfoResponse.statements.isEmpty) { + None + } else { + getOntologyInfoResponse.statements.get( + internalOntologyIri.toString + ) match { + case Some(statements: Seq[(IRI, String)]) => + val statementMap: Map[IRI, Seq[String]] = statements.groupBy { + case (pred, _) => pred + }.map { case (pred, predStatements) => + pred -> predStatements.map { case (_, obj) => + obj + } + } + + val projectIris: Seq[String] = statementMap.getOrElse( + OntologyConstants.KnoraBase.AttachedToProject, + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has no knora-base:attachedToProject" + ) + ) + val labels: Seq[String] = statementMap.getOrElse( + OntologyConstants.Rdfs.Label, + Seq.empty[String] + ) + val comments: Seq[String] = statementMap.getOrElse( + OntologyConstants.Rdfs.Comment, + Seq.empty[String] + ) + val lastModDates: Seq[String] = + statementMap.getOrElse( + OntologyConstants.KnoraBase.LastModificationDate, + Seq.empty[String] + ) + + val projectIri = if (projectIris.size > 1) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has more than one knora-base:attachedToProject" + ) + } else { + projectIris.head.toSmartIri + } + + if (!internalOntologyIri.isKnoraBuiltInDefinitionIri) { + if ( + projectIri.toString == OntologyConstants.KnoraAdmin.SystemProject + ) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri cannot be in project ${OntologyConstants.KnoraAdmin.SystemProject}" + ) + } + + if ( + internalOntologyIri.isKnoraSharedDefinitionIri && projectIri.toString != OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject + ) { + throw InconsistentRepositoryDataException( + s"Shared ontology $internalOntologyIri must be in project ${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}" + ) + } + } + + val label: String = if (labels.size > 1) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has more than one rdfs:label" + ) + } else if (labels.isEmpty) { + internalOntologyIri.getOntologyName + } else { + labels.head + } + + val comment: Option[String] = if (comments.size > 1) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has more than one rdfs:comment" + ) + } else comments.headOption + + val lastModificationDate: Option[Instant] = + if (lastModDates.size > 1) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has more than one ${OntologyConstants.KnoraBase.LastModificationDate}" + ) + } else if (lastModDates.isEmpty) { + None + } else { + val dateStr = lastModDates.head + Some( + stringFormatter.xsdDateTimeStampToInstant( + dateStr, + throw InconsistentRepositoryDataException( + s"Invalid ${OntologyConstants.KnoraBase.LastModificationDate}: $dateStr" + ) + ) + ) + } + + Some( + OntologyMetadataV2( + ontologyIri = internalOntologyIri, + projectIri = Some(projectIri), + label = Some(label), + comment = comment, + lastModificationDate = lastModificationDate + ) + ) + + case None => None + } + } + } yield metadata + } + + /** + * Given a list of ontology graphs, finds the IRIs of all subjects whose `rdf:type` is contained in a given set of types. + * + * @param ontologyGraphs a list of ontology graphs. + * @param entityTypes the types of entities to be found. + * @return a map of ontology IRIs to sets of the IRIs of entities with matching types in each ontology. + */ + def getEntityIrisFromOntologyGraphs(ontologyGraphs: Iterable[OntologyGraph], entityTypes: Set[IRI])(implicit + stringFormatter: StringFormatter + ): Map[SmartIri, Set[SmartIri]] = { + val entityTypesAsIriLiterals = entityTypes.map(entityType => IriLiteralV2(entityType)) + + ontologyGraphs.map { ontologyGraph => + val entityIrisInGraph: Set[SmartIri] = + ontologyGraph.constructResponse.statements.foldLeft(Set.empty[SmartIri]) { + case (acc, (subjectIri: IriSubjectV2, subjectStatements: Map[SmartIri, Seq[LiteralV2]])) => + val subjectTypeLiterals: Seq[IriLiteralV2] = subjectStatements + .getOrElse( + OntologyConstants.Rdf.Type.toSmartIri, + throw InconsistentRepositoryDataException(s"Subject $subjectIri has no rdf:type") + ) + .collect { case iriLiteral: IriLiteralV2 => + iriLiteral + } + + if (subjectTypeLiterals.exists(entityTypesAsIriLiterals.contains)) { + acc + subjectIri.value.toSmartIri + } else { + acc + } + + case (acc, _) => acc + } + + ontologyGraph.ontologyIri -> entityIrisInGraph + }.toMap + } + + /** + * Constructs a map of class IRIs to [[ReadClassInfoV2]] instances, based on class definitions loaded from the + * triplestore. + * + * @param classDefs a map of class IRIs to class definitions. + * @param directClassCardinalities a map of the cardinalities defined directly on each class. Each resource class + * IRI points to a map of property IRIs to [[KnoraCardinalityInfo]] objects. + * @param classCardinalitiesWithInheritance a map of the cardinalities defined directly on each class or inherited from + * base classes. Each class IRI points to a map of property IRIs to + * [[KnoraCardinalityInfo]] objects. + * @param directSubClassOfRelations a map of class IRIs to their immediate base classes. + * @param allSubClassOfRelations a map of class IRIs to all their base classes. + * @param allSubPropertyOfRelations a map of property IRIs to all their base properties. + * @param allPropertyDefs a map of property IRIs to property definitions. + * @param allKnoraResourceProps a set of the IRIs of all Knora resource properties. + * @param allLinkProps a set of the IRIs of all link properties. + * @param allLinkValueProps a set of the IRIs of link value properties. + * @param allFileValueProps a set of the IRIs of all file value properties. + * @return a map of resource class IRIs to their definitions. + */ + def makeReadClassInfos( + classDefs: Map[SmartIri, ClassInfoContentV2], + directClassCardinalities: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]], + classCardinalitiesWithInheritance: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]], + directSubClassOfRelations: Map[SmartIri, Set[SmartIri]], + allSubClassOfRelations: Map[SmartIri, Seq[SmartIri]], + allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], + allPropertyDefs: Map[SmartIri, PropertyInfoContentV2], + allKnoraResourceProps: Set[SmartIri], + allLinkProps: Set[SmartIri], + allLinkValueProps: Set[SmartIri], + allFileValueProps: Set[SmartIri] + )(implicit stringFormatter: StringFormatter): Map[SmartIri, ReadClassInfoV2] = { + classDefs.map { case (classIri, classDef) => + val ontologyIri = classIri.getOntologyFromEntity + + // Get the OWL cardinalities for the class. + val allOwlCardinalitiesForClass: Map[SmartIri, KnoraCardinalityInfo] = + classCardinalitiesWithInheritance(classIri) + val allPropertyIrisForCardinalitiesInClass: Set[SmartIri] = allOwlCardinalitiesForClass.keys.toSet + + // Identify the Knora resource properties, link properties, link value properties, and file value properties in the cardinalities. + val knoraResourcePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allKnoraResourceProps) + val linkPropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allLinkProps) + val linkValuePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allLinkValueProps) + val fileValuePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allFileValueProps) + + // Make sure there is a link value property for each link property. + + val missingLinkValueProps = linkPropsInClass.map(_.fromLinkPropToLinkValueProp) -- linkValuePropsInClass + + if (missingLinkValueProps.nonEmpty) { + throw InconsistentRepositoryDataException( + s"Resource class $classIri has cardinalities for one or more link properties without corresponding link value properties. The missing (or incorrectly defined) property or properties: ${missingLinkValueProps + .mkString(", ")}" + ) + } + + // Make sure there is a link property for each link value property. + + val missingLinkProps = linkValuePropsInClass.map(_.fromLinkValuePropToLinkProp) -- linkPropsInClass + + if (missingLinkProps.nonEmpty) { + throw InconsistentRepositoryDataException( + s"Resource class $classIri has cardinalities for one or more link value properties without corresponding link properties. The missing (or incorrectly defined) property or properties: ${missingLinkProps + .mkString(", ")}" + ) + } + + // Make sure that the cardinality for each link property is the same as the cardinality for the corresponding link value property. + for (linkProp <- linkPropsInClass) { + val linkValueProp: SmartIri = linkProp.fromLinkPropToLinkValueProp + val linkPropCardinality: KnoraCardinalityInfo = allOwlCardinalitiesForClass(linkProp) + val linkValuePropCardinality: KnoraCardinalityInfo = allOwlCardinalitiesForClass(linkValueProp) + + if (!linkPropCardinality.equalsWithoutGuiOrder(linkValuePropCardinality)) { + throw InconsistentRepositoryDataException( + s"In class $classIri, the cardinality for $linkProp is different from the cardinality for $linkValueProp" + ) + } + } + + // The class's direct cardinalities. + val directCardinalities: Map[SmartIri, KnoraCardinalityInfo] = directClassCardinalities(classIri) + + val directCardinalityPropertyIris = directCardinalities.keySet + val allBaseClasses: Seq[SmartIri] = allSubClassOfRelations(classIri) + val isKnoraResourceClass = allBaseClasses.contains(OntologyConstants.KnoraBase.Resource.toSmartIri) + val isStandoffClass = allBaseClasses.contains(OntologyConstants.KnoraBase.StandoffTag.toSmartIri) + val isValueClass = !(isKnoraResourceClass || isStandoffClass) && allBaseClasses.contains( + OntologyConstants.KnoraBase.Value.toSmartIri + ) + + // If the class is defined in project-specific ontology, do the following checks. + if (!ontologyIri.isKnoraBuiltInDefinitionIri) { + // It must be either a resource class or a standoff class, but not both. + if (!(isKnoraResourceClass ^ isStandoffClass)) { + throw InconsistentRepositoryDataException( + s"Class $classIri must be a subclass either of knora-base:Resource or of knora-base:StandoffTag (but not both)" + ) + } + + // All its cardinalities must be on properties that are defined. + val cardinalitiesOnMissingProps = directCardinalityPropertyIris.filterNot(allPropertyDefs.keySet) + + if (cardinalitiesOnMissingProps.nonEmpty) { + throw InconsistentRepositoryDataException( + s"Class $classIri has one or more cardinalities on undefined properties: ${cardinalitiesOnMissingProps + .mkString(", ")}" + ) + } + + // It cannot have cardinalities both on property P and on a subproperty of P. + + val maybePropertyAndSubproperty: Option[(SmartIri, SmartIri)] = findPropertyAndSubproperty( + propertyIris = allPropertyIrisForCardinalitiesInClass, + subPropertyOfRelations = allSubPropertyOfRelations + ) + + maybePropertyAndSubproperty match { + case Some((basePropertyIri, propertyIri)) => + throw InconsistentRepositoryDataException( + s"Class $classIri has a cardinality on property $basePropertyIri and on its subproperty $propertyIri" + ) + + case None => () + } + + if (isKnoraResourceClass) { + // If it's a resource class, all its directly defined cardinalities must be on Knora resource properties, not including knora-base:resourceProperty or knora-base:hasValue. + + val cardinalitiesOnInvalidProps = directCardinalityPropertyIris.filterNot(allKnoraResourceProps) + + if (cardinalitiesOnInvalidProps.nonEmpty) { + throw InconsistentRepositoryDataException( + s"Resource class $classIri has one or more cardinalities on properties that are not Knora resource properties: ${cardinalitiesOnInvalidProps + .mkString(", ")}" + ) + } + + Set(OntologyConstants.KnoraBase.ResourceProperty, OntologyConstants.KnoraBase.HasValue).foreach { + invalidProp => + if (directCardinalityPropertyIris.contains(invalidProp.toSmartIri)) { + throw InconsistentRepositoryDataException( + s"Class $classIri has a cardinality on property $invalidProp, which is not allowed" + ) + } + } + + // Check for invalid cardinalities on boolean properties. + checkForInvalidBooleanCardinalities( + classIri = classIri, + directCardinalities = directCardinalities, + allPropertyDefs = allPropertyDefs, + schemaForErrors = InternalSchema, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) + + // All its base classes with Knora IRIs must also be resource classes. + for (baseClass <- classDef.subClassOf) { + if ( + baseClass.isKnoraDefinitionIri && !allSubClassOfRelations(baseClass).contains( + OntologyConstants.KnoraBase.Resource.toSmartIri + ) + ) { + throw InconsistentRepositoryDataException( + s"Class $classIri is a subclass of knora-base:Resource, but its base class $baseClass is not" + ) + } + } + + // It must have an rdfs:label. + if (!classDef.predicates.contains(OntologyConstants.Rdfs.Label.toSmartIri)) { + throw InconsistentRepositoryDataException(s"Class $classIri has no rdfs:label") + } + } else { + // If it's a standoff class, none of its cardinalities must be on Knora resource properties. + + val cardinalitiesOnInvalidProps = directCardinalityPropertyIris.filter(allKnoraResourceProps) + + if (cardinalitiesOnInvalidProps.nonEmpty) { + throw InconsistentRepositoryDataException( + s"Standoff class $classIri has one or more cardinalities on properties that are Knora resource properties: ${cardinalitiesOnInvalidProps + .mkString(", ")}" + ) + } + + // All its base classes with Knora IRIs must also be standoff classes. + for (baseClass <- classDef.subClassOf) { + if (baseClass.isKnoraDefinitionIri) { + if ( + isStandoffClass && !allSubClassOfRelations(baseClass).contains( + OntologyConstants.KnoraBase.StandoffTag.toSmartIri + ) + ) { + throw InconsistentRepositoryDataException( + s"Class $classIri is a subclass of knora-base:StandoffTag, but its base class $baseClass is not" + ) + } + } + } + } + } + + // Each class must be a subclass of all the classes that are subject class constraints of the properties in its cardinalities. + checkSubjectClassConstraintsViaCardinalities( + internalClassDef = classDef, + allBaseClassIris = allBaseClasses.toSet, + allClassCardinalityKnoraPropertyDefs = + allPropertyDefs.view.filterKeys(allOwlCardinalitiesForClass.keySet).toMap, + errorSchema = InternalSchema, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) + + val inheritedCardinalities: Map[SmartIri, KnoraCardinalityInfo] = allOwlCardinalitiesForClass.filterNot { + case (propertyIri, _) => directCardinalityPropertyIris.contains(propertyIri) + } + + // Get the class's standoff data type, if any. A standoff class that has a datatype is a subclass of one of the classes + // in StandoffDataTypeClasses. + + val standoffDataType: Set[SmartIri] = allSubClassOfRelations(classIri).toSet + .intersect(StandoffDataTypeClasses.getStandoffClassIris.map(_.toSmartIri)) + + if (standoffDataType.size > 1) { + throw InconsistentRepositoryDataException( + s"Class $classIri is a subclass of more than one standoff datatype: ${standoffDataType.mkString(", ")}" + ) + } + + // A class can be instantiated if it's in a built-in ontology and marked with knora-base:canBeInstantiated, or if it's + // a resource class in a project-specific ontology. + val canBeInstantiated = if (ontologyIri.isKnoraBuiltInDefinitionIri) { + classDef.predicates + .get(OntologyConstants.KnoraBase.CanBeInstantiated.toSmartIri) + .flatMap(_.objects.headOption) match { + case Some(booleanLiteral: BooleanLiteralV2) => booleanLiteral.value + case _ => false + } + } else { + isKnoraResourceClass + } + + val readClassInfo = ReadClassInfoV2( + entityInfoContent = classDef, + allBaseClasses = allBaseClasses, + isResourceClass = isKnoraResourceClass, + isStandoffClass = isStandoffClass, + isValueClass = isValueClass, + canBeInstantiated = canBeInstantiated, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = knoraResourcePropsInClass, + linkProperties = linkPropsInClass, + linkValueProperties = linkValuePropsInClass, + fileValueProperties = fileValuePropsInClass, + standoffDataType = standoffDataType.headOption match { + case Some(dataType: SmartIri) => + Some( + StandoffDataTypeClasses.lookup( + dataType.toString, + throw InconsistentRepositoryDataException(s"$dataType is not a valid standoff datatype") + ) + ) + + case None => None + } + ) + + classIri -> readClassInfo + } + } + + /** + * Checks that a class is a subclass of all the classes that are subject class constraints of the Knora resource properties in its cardinalities. + * + * @param internalClassDef the class definition. + * @param allBaseClassIris the IRIs of all the class's base classes. + * @param allClassCardinalityKnoraPropertyDefs the definitions of all the Knora resource properties on which the class has cardinalities (whether directly defined + * or inherited). + * @param errorSchema the ontology schema to be used in error messages. + * @param errorFun a function that throws an exception. It will be called with an error message argument if the cardinalities are invalid. + */ + private def checkSubjectClassConstraintsViaCardinalities( + internalClassDef: ClassInfoContentV2, + allBaseClassIris: Set[SmartIri], + allClassCardinalityKnoraPropertyDefs: Map[SmartIri, PropertyInfoContentV2], + errorSchema: OntologySchema, + errorFun: String => Nothing + )(implicit stringFormatter: StringFormatter): Unit = + allClassCardinalityKnoraPropertyDefs.foreach { case (propertyIri, propertyDef) => + propertyDef.predicates.get(OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { + case Some(subjectClassConstraintPred) => + val subjectClassConstraint = subjectClassConstraintPred.requireIriObject( + throw InconsistentRepositoryDataException( + s"Property $propertyIri has an invalid object for ${OntologyConstants.KnoraBase.SubjectClassConstraint}" + ) + ) + + if (!allBaseClassIris.contains(subjectClassConstraint)) { + val hasOrWouldInherit = if (internalClassDef.directCardinalities.contains(propertyIri)) { + "has" + } else { + "would inherit" + } + + errorFun( + s"Class ${internalClassDef.classIri.toOntologySchema(errorSchema)} $hasOrWouldInherit a cardinality for property ${propertyIri + .toOntologySchema(errorSchema)}, but is not a subclass of that property's ${OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri + .toOntologySchema(errorSchema)}, ${subjectClassConstraint.toOntologySchema(errorSchema)}" + ) + } + + case None => () + } + } + + /** + * Checks for invalid cardinalities on boolean properties. + * + * @param classIri the class IRI. + * @param directCardinalities the cardinalities directly defined on the class. + * @param allPropertyDefs all property definitions. + */ + def checkForInvalidBooleanCardinalities( + classIri: SmartIri, + directCardinalities: Map[SmartIri, KnoraCardinalityInfo], + allPropertyDefs: Map[SmartIri, PropertyInfoContentV2], + schemaForErrors: OntologySchema, + errorFun: String => Nothing + )(implicit stringFormatter: StringFormatter): Unit = { + // A cardinality on a property with a boolean object must be 1 or 0-1. + + val invalidCardinalitiesOnBooleanProps: Set[SmartIri] = directCardinalities.filter { + case (propertyIri, knoraCardinalityInfo) => + val objectClassConstraintIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri + + val propertyObjectClassConstraint: SmartIri = allPropertyDefs(propertyIri).requireIriObject( + objectClassConstraintIri, + errorFun(s"Property ${propertyIri + .toOntologySchema(schemaForErrors)} has no ${objectClassConstraintIri.toOntologySchema(schemaForErrors)}") + ) + + propertyObjectClassConstraint == OntologyConstants.KnoraBase.BooleanValue.toSmartIri && + !(knoraCardinalityInfo.cardinality == Cardinality.MustHaveOne || knoraCardinalityInfo.cardinality == Cardinality.MayHaveOne) + }.keySet + + if (invalidCardinalitiesOnBooleanProps.nonEmpty) { + errorFun( + s"Class ${classIri.toOntologySchema(schemaForErrors).toSparql} has one or more invalid cardinalities on boolean properties: ${invalidCardinalitiesOnBooleanProps + .map(_.toOntologySchema(schemaForErrors).toSparql) + .mkString(", ")}" + ) + } + } + + /** + * Constructs a map of property IRIs to [[ReadPropertyInfoV2]] instances, based on property definitions loaded from the + * triplestore. + * + * @param propertyDefs a map of property IRIs to property definitions. + * @param directSubPropertyOfRelations a map of property IRIs to their immediate base properties. + * @param allSubPropertyOfRelations a map of property IRIs to all their base properties. + * @param allSubClassOfRelations a map of class IRIs to all their base classes. + * @param allGuiAttributeDefinitions a map of `Guielement` IRIs to sets of [[SalsahGuiAttributeDefinition]]. + * @param allKnoraResourceProps a set of the IRIs of all Knora resource properties. + * @param allLinkProps a set of the IRIs of all link properties. + * @param allLinkValueProps a set of the IRIs of link value properties. + * @param allFileValueProps a set of the IRIs of all file value properties. + * @return a map of property IRIs to [[ReadPropertyInfoV2]] instances. + */ + def makeReadPropertyInfos( + propertyDefs: Map[SmartIri, PropertyInfoContentV2], + directSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], + allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], + allSubClassOfRelations: Map[SmartIri, Seq[SmartIri]], + allGuiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]], + allKnoraResourceProps: Set[SmartIri], + allLinkProps: Set[SmartIri], + allLinkValueProps: Set[SmartIri], + allFileValueProps: Set[SmartIri] + )(implicit stringFormatter: StringFormatter): Map[SmartIri, ReadPropertyInfoV2] = + propertyDefs.map { case (propertyIri, propertyDef) => + val ontologyIri = propertyIri.getOntologyFromEntity + + validateGuiAttributes( + propertyInfoContent = propertyDef, + allGuiAttributeDefinitions = allGuiAttributeDefinitions, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) + + val isResourceProp = allKnoraResourceProps.contains(propertyIri) + val isValueProp = + allSubPropertyOfRelations(propertyIri).contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) + val isLinkProp = allLinkProps.contains(propertyIri) + val isLinkValueProp = allLinkValueProps.contains(propertyIri) + val isFileValueProp = allFileValueProps.contains(propertyIri) + + // If the property is defined in a project-specific ontology and is a Knora resource property (a subproperty of knora-base:hasValue or knora-base:hasLinkTo), do the following checks. + if (!propertyIri.isKnoraBuiltInDefinitionIri && isResourceProp) { + // The property must be a subproperty of knora-base:hasValue or knora-base:hasLinkTo, but not both. + if (isValueProp && isLinkProp) { + throw InconsistentRepositoryDataException( + s"Property $propertyIri cannot be a subproperty of both knora-base:hasValue and knora-base:hasLinkTo" + ) + } + + // It can't be a subproperty of knora-base:hasFileValue. + if (isFileValueProp) { + throw InconsistentRepositoryDataException( + s"Property $propertyIri cannot be a subproperty of knora-base:hasFileValue" + ) + } + + // Each of its base properties that has a Knora IRI must also be a Knora resource property. + for (baseProperty <- propertyDef.subPropertyOf) { + if (baseProperty.isKnoraDefinitionIri && !allKnoraResourceProps.contains(baseProperty)) { + throw InconsistentRepositoryDataException( + s"Property $propertyIri is a subproperty of knora-base:hasValue or knora-base:hasLinkTo, but its base property $baseProperty is not" + ) + } + } + + // It must have an rdfs:label. + if (!propertyDef.predicates.contains(OntologyConstants.Rdfs.Label.toSmartIri)) { + throw InconsistentRepositoryDataException(s"Property $propertyIri has no rdfs:label") + } + } + + // A property is editable if it's in a built-in ontology and marked with knora-base:isEditable, + // or if it's a resource property in a project-specific ontology. + val isEditable = if (ontologyIri.isKnoraBuiltInDefinitionIri) { + propertyDef.predicates + .get(OntologyConstants.KnoraBase.IsEditable.toSmartIri) + .flatMap(_.objects.headOption) match { + case Some(booleanLiteral: BooleanLiteralV2) => booleanLiteral.value + case _ => false + } + } else { + isResourceProp + } + + val propertyEntityInfo = ReadPropertyInfoV2( + entityInfoContent = propertyDef, + isResourceProp = isResourceProp, + isEditable = isEditable, + isLinkProp = isLinkProp, + isLinkValueProp = isLinkValueProp, + isFileValueProp = isFileValueProp, + isStandoffInternalReferenceProperty = allSubPropertyOfRelations(propertyIri).contains( + OntologyConstants.KnoraBase.StandoffTagHasInternalReference.toSmartIri + ) + ) + + propertyIri -> propertyEntityInfo + } + + /** + * Constructs a map of OWL named individual IRIs to [[ReadIndividualInfoV2]] instances. + * + * @param individualDefs a map of OWL named individual IRIs to named individuals. + * @return a map of individual IRIs to [[ReadIndividualInfoV2]] instances. + */ + def makeReadIndividualInfos( + individualDefs: Map[SmartIri, IndividualInfoContentV2] + ): Map[SmartIri, ReadIndividualInfoV2] = + individualDefs.map { case (individualIri, individual) => + individualIri -> ReadIndividualInfoV2(individual) + } + + /** + * Given all the OWL named individuals available, constructs a map of `salsah-gui:Guielement` individuals to + * their GUI attribute definitions. + * + * @param allIndividuals all the OWL named individuals available. + * @return a map of `salsah-gui:Guielement` individuals to their GUI attribute definitions. + */ + def makeGuiAttributeDefinitions( + allIndividuals: Map[SmartIri, IndividualInfoContentV2] + )(implicit stringFormatter: StringFormatter): Map[SmartIri, Set[SalsahGuiAttributeDefinition]] = { + val guiElementIndividuals: Map[SmartIri, IndividualInfoContentV2] = allIndividuals.filter { case (_, individual) => + individual.getRdfType.toString == OntologyConstants.SalsahGui.GuiElementClass + } + + guiElementIndividuals.map { case (guiElementIri, guiElementIndividual) => + val attributeDefs: Set[SalsahGuiAttributeDefinition] = + guiElementIndividual.predicates.get(OntologyConstants.SalsahGui.GuiAttributeDefinition.toSmartIri) match { + case Some(predicateInfo) => + predicateInfo.objects.map { + case StringLiteralV2(attributeDefStr, None) => + stringFormatter.toSalsahGuiAttributeDefinition( + attributeDefStr, + throw InconsistentRepositoryDataException( + s"Invalid salsah-gui:guiAttributeDefinition in $guiElementIri: $attributeDefStr" + ) + ) + + case other => + throw InconsistentRepositoryDataException( + s"Invalid salsah-gui:guiAttributeDefinition in $guiElementIri: $other" + ) + }.toSet + + case None => Set.empty[SalsahGuiAttributeDefinition] + } + + guiElementIri -> attributeDefs + } + } + + /** + * Validates the GUI attributes of a resource class property. + * + * @param propertyInfoContent the property definition. + * @param allGuiAttributeDefinitions the GUI attribute definitions for each GUI element. + * @param errorFun a function that throws an exception. It will be passed the message to be included in the exception. + */ + def validateGuiAttributes( + propertyInfoContent: PropertyInfoContentV2, + allGuiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]], + errorFun: String => Nothing + )(implicit stringFormatter: StringFormatter): Unit = { + val propertyIri = propertyInfoContent.propertyIri + val predicates = propertyInfoContent.predicates + + // Find out which salsah-gui:Guielement the property uses, if any. + val maybeGuiElementPred: Option[PredicateInfoV2] = + predicates.get(OntologyConstants.SalsahGui.GuiElementProp.toSmartIri) + val maybeGuiElementIri: Option[SmartIri] = maybeGuiElementPred.map( + _.requireIriObject( + throw InconsistentRepositoryDataException( + s"Property $propertyIri has an invalid object for ${OntologyConstants.SalsahGui.GuiElementProp}" + ) + ) + ) + + // Get that Guielement's attribute definitions, if any. + val guiAttributeDefs: Set[SalsahGuiAttributeDefinition] = maybeGuiElementIri match { + case Some(guiElementIri) => + allGuiAttributeDefinitions.getOrElse( + guiElementIri, + errorFun(s"Property $propertyIri has salsah-gui:guiElement $guiElementIri, which doesn't exist") + ) + + case None => Set.empty[SalsahGuiAttributeDefinition] + } + + // If the property has the predicate salsah-gui:guiAttribute, syntactically validate the objects of that predicate. + val guiAttributes: Set[SalsahGuiAttribute] = + predicates.get(OntologyConstants.SalsahGui.GuiAttribute.toSmartIri) match { + case Some(guiAttributePred) => + val guiElementIri = maybeGuiElementIri.getOrElse( + errorFun(s"Property $propertyIri has salsah-gui:guiAttribute, but no salsah-gui:guiElement") + ) + + if (guiAttributeDefs.isEmpty) { + errorFun( + s"Property $propertyIri has salsah-gui:guiAttribute, but $guiElementIri has no salsah-gui:guiAttributeDefinition" + ) + } + + // Syntactically validate each attribute. + guiAttributePred.objects.map { + case StringLiteralV2(guiAttributeObj, None) => + stringFormatter.toSalsahGuiAttribute( + s = guiAttributeObj, + attributeDefs = guiAttributeDefs, + errorFun = + errorFun(s"Property $propertyIri contains an invalid salsah-gui:guiAttribute: $guiAttributeObj") + ) + + case other => + errorFun(s"Property $propertyIri contains an invalid salsah-gui:guiAttribute: $other") + }.toSet + + case None => Set.empty[SalsahGuiAttribute] + } + + // Check that all required GUI attributes are provided. + val requiredAttributeNames = guiAttributeDefs.filter(_.isRequired).map(_.attributeName) + val providedAttributeNames = guiAttributes.map(_.attributeName) + val missingAttributeNames: Set[String] = requiredAttributeNames -- providedAttributeNames + + if (missingAttributeNames.nonEmpty) { + errorFun( + s"Property $propertyIri has one or more missing objects of salsah-gui:guiAttribute: ${missingAttributeNames + .mkString(", ")}" + ) + } + } + + /** + * Before creating a new class or adding cardinalities to an existing class, checks the validity of the + * cardinalities directly defined on the class. Adds link value properties for the corresponding + * link properties. + * + * @param internalClassDef the internal definition of the class. + * @param allBaseClassIris the IRIs of all the class's base classes, including the class itself. + * @param cacheData the ontology cache. + * @param existingLinkPropsToKeep the link properties that are already defined on the class and that + * will be kept after the update. + * @return the updated class definition, and the cardinalities resulting from inheritance. + */ + def checkCardinalitiesBeforeAdding( + internalClassDef: ClassInfoContentV2, + allBaseClassIris: Set[SmartIri], + cacheData: OntologyCacheData, + existingLinkPropsToKeep: Set[SmartIri] = Set.empty + )(implicit stringFormatter: StringFormatter): (ClassInfoContentV2, Map[SmartIri, KnoraCardinalityInfo]) = { + // If the class has cardinalities, check that the properties are already defined as Knora properties. + + val propertyDefsForDirectCardinalities: Set[ReadPropertyInfoV2] = internalClassDef.directCardinalities.keySet.map { + propertyIri => + if ( + !isKnoraResourceProperty( + propertyIri, + cacheData + ) || propertyIri.toString == OntologyConstants.KnoraBase.ResourceProperty || propertyIri.toString == OntologyConstants.KnoraBase.HasValue + ) { + throw NotFoundException(s"Invalid property for cardinality: <${propertyIri.toOntologySchema(ApiV2Complex)}>") + } + + cacheData.ontologies(propertyIri.getOntologyFromEntity).properties(propertyIri) + } + + val existingLinkValuePropsToKeep = existingLinkPropsToKeep.map(_.fromLinkPropToLinkValueProp) + val newLinkPropsInClass: Set[SmartIri] = propertyDefsForDirectCardinalities + .filter(_.isLinkProp) + .map(_.entityInfoContent.propertyIri) -- existingLinkValuePropsToKeep + val newLinkValuePropsInClass: Set[SmartIri] = propertyDefsForDirectCardinalities + .filter(_.isLinkValueProp) + .map(_.entityInfoContent.propertyIri) -- existingLinkValuePropsToKeep + + // Don't allow link value prop cardinalities to be included in the request. + + if (newLinkValuePropsInClass.nonEmpty) { + throw BadRequestException( + s"In class ${internalClassDef.classIri.toOntologySchema(ApiV2Complex)}, cardinalities have been submitted for one or more link value properties: ${newLinkValuePropsInClass + .map(_.toOntologySchema(ApiV2Complex)) + .mkString(", ")}. Just submit the link properties, and the link value properties will be included automatically." + ) + } + + // Add a link value prop cardinality for each new link prop cardinality. + + val linkValuePropCardinalitiesToAdd: Map[SmartIri, KnoraCardinalityInfo] = newLinkPropsInClass.map { linkPropIri => + val linkValuePropIri = linkPropIri.fromLinkPropToLinkValueProp + + // Ensure that the link value prop exists. + cacheData + .ontologies(linkValuePropIri.getOntologyFromEntity) + .properties + .getOrElse(linkValuePropIri, throw NotFoundException(s"Link value property <$linkValuePropIri> not found")) + + linkValuePropIri -> internalClassDef.directCardinalities(linkPropIri) + }.toMap + + val classDefWithAddedLinkValueProps: ClassInfoContentV2 = internalClassDef.copy( + directCardinalities = internalClassDef.directCardinalities ++ linkValuePropCardinalitiesToAdd + ) + + // Get the cardinalities that the class can inherit. + + val cardinalitiesAvailableToInherit: Map[SmartIri, KnoraCardinalityInfo] = + classDefWithAddedLinkValueProps.subClassOf.flatMap { baseClassIri => + cacheData.ontologies(baseClassIri.getOntologyFromEntity).classes(baseClassIri).allCardinalities + }.toMap + + // Check that the cardinalities directly defined on the class are compatible with any inheritable + // cardinalities, and let directly-defined cardinalities override cardinalities in base classes. + + val cardinalitiesForClassWithInheritance: Map[SmartIri, KnoraCardinalityInfo] = overrideCardinalities( + classIri = internalClassDef.classIri, + thisClassCardinalities = classDefWithAddedLinkValueProps.directCardinalities, + inheritableCardinalities = cardinalitiesAvailableToInherit, + allSubPropertyOfRelations = cacheData.subPropertyOfRelations, + errorSchema = ApiV2Complex, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) + + // Check that the class is a subclass of all the classes that are subject class constraints of the Knora resource properties in its cardinalities. + + val knoraResourcePropertyIrisInCardinalities = cardinalitiesForClassWithInheritance.keySet.filter { propertyIri => + isKnoraResourceProperty( + propertyIri = propertyIri, + cacheData = cacheData + ) + } + + val allClassCardinalityKnoraPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = + knoraResourcePropertyIrisInCardinalities.map { propertyIri => + propertyIri -> cacheData.ontologies(propertyIri.getOntologyFromEntity).properties(propertyIri).entityInfoContent + }.toMap + + checkSubjectClassConstraintsViaCardinalities( + internalClassDef = classDefWithAddedLinkValueProps, + allBaseClassIris = allBaseClassIris, + allClassCardinalityKnoraPropertyDefs = allClassCardinalityKnoraPropertyDefs, + errorSchema = ApiV2Complex, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) + + // It cannot have cardinalities both on property P and on a subproperty of P. + + val maybePropertyAndSubproperty: Option[(SmartIri, SmartIri)] = findPropertyAndSubproperty( + propertyIris = cardinalitiesForClassWithInheritance.keySet, + subPropertyOfRelations = cacheData.subPropertyOfRelations + ) + + maybePropertyAndSubproperty match { + case Some((basePropertyIri, propertyIri)) => + throw BadRequestException( + s"Class <${classDefWithAddedLinkValueProps.classIri.toOntologySchema(ApiV2Complex)}> has a cardinality on property <${basePropertyIri + .toOntologySchema(ApiV2Complex)}> and on its subproperty <${propertyIri.toOntologySchema(ApiV2Complex)}>" + ) + + case None => () + } + + // Check for invalid cardinalities on boolean properties. + checkForInvalidBooleanCardinalities( + classIri = internalClassDef.classIri, + directCardinalities = internalClassDef.directCardinalities, + allPropertyDefs = cacheData.allPropertyDefs, + schemaForErrors = ApiV2Complex, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) + + (classDefWithAddedLinkValueProps, cardinalitiesForClassWithInheritance) + } + + /** + * Given a set of property IRIs, determines whether the set contains a property P and a subproperty of P. + * + * @param propertyIris the set of property IRIs. + * @param subPropertyOfRelations all the subproperty relations in the triplestore. + * @return a property and its subproperty, if found. + */ + private def findPropertyAndSubproperty( + propertyIris: Set[SmartIri], + subPropertyOfRelations: Map[SmartIri, Set[SmartIri]] + ): Option[(SmartIri, SmartIri)] = + propertyIris.flatMap { propertyIri => + val maybeBasePropertyIri: Option[SmartIri] = (propertyIris - propertyIri).find { otherPropertyIri => + subPropertyOfRelations.get(propertyIri).exists { baseProperties: Set[SmartIri] => + baseProperties.contains(otherPropertyIri) + } + } + + maybeBasePropertyIri.map { basePropertyIri => + (basePropertyIri, propertyIri) + } + }.headOption + + /** + * Gets the set of subjects that refer to an ontology or its entities. + * + * @param ontology the ontology. + * @return the set of subjects that refer to the ontology or its entities. + */ + def getSubjectsUsingOntology(settings: KnoraSettingsImpl, storeManager: ActorRef, ontology: ReadOntologyV2)(implicit + ec: ExecutionContext, + timeout: Timeout + ): Future[Set[IRI]] = + for { + isOntologyUsedSparql <- Future( + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .isOntologyUsed( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = ontology.ontologyMetadata.ontologyIri, + classIris = ontology.classes.keySet, + propertyIris = ontology.properties.keySet + ) + .toString() + ) + + isOntologyUsedResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(isOntologyUsedSparql)) + .mapTo[SparqlSelectResult] + + subjects = isOntologyUsedResponse.results.bindings.map { row => + row.rowMap("s") + }.toSet + } yield subjects + + /** + * Before an update of an ontology entity, checks that the entity's external IRI, and that of its ontology, + * are valid, and checks that the user has permission to update the ontology. + * + * @param externalOntologyIri the external IRI of the ontology. + * @param externalEntityIri the external IRI of the entity. + * @param requestingUser the user making the request. + */ + def checkOntologyAndEntityIrisForUpdate( + externalOntologyIri: SmartIri, + externalEntityIri: SmartIri, + requestingUser: UserADM + )(implicit ex: ExecutionContext): Future[Unit] = + for { + _ <- checkExternalOntologyIriForUpdate(externalOntologyIri) + _ <- checkExternalEntityIriForUpdate(externalEntityIri = externalEntityIri) + _ <- checkPermissionsForOntologyUpdate( + internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema), + requestingUser = requestingUser + ) + } yield () + + /** + * Loads a property definition from the triplestore and converts it to a [[PropertyInfoContentV2]]. + * + * @param propertyIri the IRI of the property to be loaded. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[PropertyInfoContentV2]] representing the property definition. + */ + def loadPropertyDefinition( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + propertyIri: SmartIri, + featureFactoryConfig: FeatureFactoryConfig + )(implicit ex: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[PropertyInfoContentV2] = + for { + sparql <- Future( + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .getPropertyDefinition( + triplestore = settings.triplestoreType, + propertyIri = propertyIri + ) + .toString() + ) + + constructResponse <- (storeManager ? SparqlExtendedConstructRequest( + sparql = sparql, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] + } yield constructResponseToPropertyDefinition( + propertyIri = propertyIri, + constructResponse = constructResponse + ) + + /** + * Given a map of predicate IRIs to predicate objects describing an entity, returns a map of smart IRIs to [[PredicateInfoV2]] + * objects that can be used to construct an [[EntityInfoContentV2]]. + * + * @param entityDefMap a map of predicate IRIs to predicate objects. + * @return a map of smart IRIs to [[PredicateInfoV2]] objects. + */ + private def getEntityPredicatesFromConstructResponse(entityDefMap: Map[SmartIri, Seq[LiteralV2]])(implicit + stringFormatter: StringFormatter + ): Map[SmartIri, PredicateInfoV2] = + entityDefMap.map { case (predicateIri: SmartIri, predObjs: Seq[LiteralV2]) => + val predicateInfo = PredicateInfoV2( + predicateIri = predicateIri, + objects = predObjs.map { + case IriLiteralV2(iriStr) => + // We use xsd:dateTime in the triplestore (because it is supported in SPARQL), but we return + // the more restrictive xsd:dateTimeStamp in the API. + if (iriStr == OntologyConstants.Xsd.DateTime) { + SmartIriLiteralV2(OntologyConstants.Xsd.DateTimeStamp.toSmartIri) + } else { + SmartIriLiteralV2(iriStr.toSmartIri) + } + + case ontoLiteral: OntologyLiteralV2 => ontoLiteral + + case other => + throw InconsistentRepositoryDataException(s"Predicate $predicateIri has an invalid object: $other") + } + ) + + predicateIri -> predicateInfo + } + + /** + * Extracts property definitions from a SPARQL CONSTRUCT response. + * + * @param propertyIris the IRIs of the properties to be read. + * @param constructResponse the SPARQL construct response to be read. + * @return a map of property IRIs to property definitions. + */ + def constructResponseToPropertyDefinitions( + propertyIris: Set[SmartIri], + constructResponse: SparqlExtendedConstructResponse + )(implicit stringFormatter: StringFormatter): Map[SmartIri, PropertyInfoContentV2] = + propertyIris.map { propertyIri => + propertyIri -> constructResponseToPropertyDefinition( + propertyIri = propertyIri, + constructResponse = constructResponse + ) + }.toMap + + /** + * Converts a SPARQL CONSTRUCT response to a [[PropertyInfoContentV2]]. + * + * @param propertyIri the IRI of the property to be read. + * @param constructResponse the SPARQL CONSTRUCT response to be read. + * @return a [[PropertyInfoContentV2]] representing a property definition. + */ + private def constructResponseToPropertyDefinition( + propertyIri: SmartIri, + constructResponse: SparqlExtendedConstructResponse + )(implicit stringFormatter: StringFormatter): PropertyInfoContentV2 = { + // All properties defined in the triplestore must be in Knora ontologies. + + val ontologyIri = propertyIri.getOntologyFromEntity + + if (!ontologyIri.isKnoraOntologyIri) { + throw InconsistentRepositoryDataException(s"Property $propertyIri is not in a Knora ontology") + } + + val statements = constructResponse.statements + + // Get the statements whose subject is the property. + val propertyDefMap: Map[SmartIri, Seq[LiteralV2]] = statements(IriSubjectV2(propertyIri.toString)) + + val subPropertyOf: Set[SmartIri] = propertyDefMap.get(OntologyConstants.Rdfs.SubPropertyOf.toSmartIri) match { + case Some(baseProperties) => + baseProperties.map { + case iriLiteral: IriLiteralV2 => iriLiteral.value.toSmartIri + case other => throw InconsistentRepositoryDataException(s"Unexpected object for rdfs:subPropertyOf: $other") + }.toSet + + case None => Set.empty[SmartIri] + } + + val otherPreds: Map[SmartIri, PredicateInfoV2] = getEntityPredicatesFromConstructResponse( + propertyDefMap - OntologyConstants.Rdfs.SubPropertyOf.toSmartIri + ) + + // salsah-gui:guiOrder isn't allowed here. + if (otherPreds.contains(OntologyConstants.SalsahGui.GuiOrder.toSmartIri)) { + throw InconsistentRepositoryDataException(s"Property $propertyIri contains salsah-gui:guiOrder") + } + + val propertyDef = PropertyInfoContentV2( + propertyIri = propertyIri, + subPropertyOf = subPropertyOf, + predicates = otherPreds, + ontologySchema = propertyIri.getOntologySchema.get + ) + + if ( + !propertyIri.isKnoraBuiltInDefinitionIri && propertyDef.getRdfTypes.contains( + OntologyConstants.Owl.TransitiveProperty.toSmartIri + ) + ) { + throw InconsistentRepositoryDataException( + s"Project-specific property $propertyIri cannot be an owl:TransitiveProperty" + ) + } + + propertyDef + } + + /** + * Reads OWL named individuals from a SPARQL CONSTRUCT response. + * + * @param individualIris the IRIs of the named individuals to be read. + * @param constructResponse the SPARQL CONSTRUCT response. + * @return a map of individual IRIs to named individuals. + */ + def constructResponseToIndividuals(individualIris: Set[SmartIri], constructResponse: SparqlExtendedConstructResponse)( + implicit stringFormatter: StringFormatter + ): Map[SmartIri, IndividualInfoContentV2] = + individualIris.map { individualIri => + individualIri -> constructResponseToIndividual( + individualIri = individualIri, + constructResponse = constructResponse + ) + }.toMap + + /** + * Reads an OWL named individual from a SPARQL CONSTRUCT response. + * + * @param individualIri the IRI of the individual to be read. + * @param constructResponse the SPARQL CONSTRUCT response. + * @return an [[IndividualInfoContentV2]] representing the named individual. + */ + private def constructResponseToIndividual( + individualIri: SmartIri, + constructResponse: SparqlExtendedConstructResponse + )(implicit stringFormatter: StringFormatter): IndividualInfoContentV2 = { + val statements = constructResponse.statements + + // Get the statements whose subject is the individual. + val individualMap: Map[SmartIri, Seq[LiteralV2]] = statements(IriSubjectV2(individualIri.toString)) + + val predicates: Map[SmartIri, PredicateInfoV2] = getEntityPredicatesFromConstructResponse(individualMap) + + IndividualInfoContentV2( + individualIri = individualIri, + predicates = predicates, + ontologySchema = individualIri.getOntologySchema.get + ) + } + + /** + * Given a list of resource IRIs and a list of property IRIs (ontology entities), returns an [[EntityInfoGetResponseV2]] describing both resource and property entities. + * + * @param classIris the IRIs of the resource entities to be queried. + * @param propertyIris the IRIs of the property entities to be queried. + * @param requestingUser the user making the request. + * @return an [[EntityInfoGetResponseV2]]. + */ + def getEntityInfoResponseV2( + classIris: Set[SmartIri] = Set.empty[SmartIri], + propertyIris: Set[SmartIri] = Set.empty[SmartIri], + requestingUser: UserADM + )(implicit ec: ExecutionContext): Future[EntityInfoGetResponseV2] = { + for { + cacheData <- Cache.getCacheData + + // See if any of the requested entities are not Knora entities. + + nonKnoraEntities = (classIris ++ propertyIris).filter(!_.isKnoraEntityIri) + + _ = if (nonKnoraEntities.nonEmpty) { + throw BadRequestException( + s"Some requested entities are not Knora entities: ${nonKnoraEntities.mkString(", ")}" + ) + } + + // See if any of the requested entities are unavailable in the requested schema. + + classesUnavailableInSchema: Set[SmartIri] = classIris.foldLeft(Set.empty[SmartIri]) { case (acc, classIri) => + // Is this class IRI hard-coded in the requested schema? + if ( + KnoraBaseToApiV2SimpleTransformationRules.externalClassesToAdd + .contains(classIri) || + KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd + .contains(classIri) + ) { + // Yes, so it's available. + acc + } else { + // No. Is it among the classes removed from the internal ontology in the requested schema? + classIri.getOntologySchema.get match { + case apiV2Schema: ApiV2Schema => + val internalClassIri = + classIri.toOntologySchema(InternalSchema) + val knoraBaseClassesToRemove = OntologyTransformationRules + .getTransformationRules( + classIri.getOntologyFromEntity, + apiV2Schema + ) + .internalClassesToRemove + + if (knoraBaseClassesToRemove.contains(internalClassIri)) { + // Yes. Include it in the set of unavailable classes. + acc + classIri + } else { + // No. It's available. + acc + } + + case InternalSchema => acc + } + } + } + + propertiesUnavailableInSchema: Set[SmartIri] = propertyIris.foldLeft(Set.empty[SmartIri]) { + case (acc, propertyIri) => + // Is this property IRI hard-coded in the requested schema? + if ( + KnoraBaseToApiV2SimpleTransformationRules.externalPropertiesToAdd + .contains(propertyIri) || + KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd + .contains(propertyIri) + ) { + // Yes, so it's available. + acc + } else { + // No. See if it's available in the requested schema. + propertyIri.getOntologySchema.get match { + case apiV2Schema: ApiV2Schema => + val internalPropertyIri = + propertyIri.toOntologySchema(InternalSchema) + + // If it's a link value property and it's requested in the simple schema, it's unavailable. + if ( + apiV2Schema == ApiV2Simple && OntologyHelpers + .isLinkValueProp(internalPropertyIri, cacheData) + ) { + acc + propertyIri + } else { + // Is it among the properties removed from the internal ontology in the requested schema? + + val knoraBasePropertiesToRemove = + OntologyTransformationRules + .getTransformationRules( + propertyIri.getOntologyFromEntity, + apiV2Schema + ) + .internalPropertiesToRemove + + if ( + knoraBasePropertiesToRemove.contains( + internalPropertyIri + ) + ) { + // Yes. Include it in the set of unavailable properties. + acc + propertyIri + } else { + // No. It's available. + acc + } + } + + case InternalSchema => acc + } + } + } + + entitiesUnavailableInSchema = classesUnavailableInSchema ++ propertiesUnavailableInSchema + + _ = if (entitiesUnavailableInSchema.nonEmpty) { + throw NotFoundException( + s"Some requested entities were not found: ${entitiesUnavailableInSchema.mkString(", ")}" + ) + } + + // See if any of the requested entities are hard-coded for knora-api. + + hardCodedExternalClassesAvailable: Map[SmartIri, ReadClassInfoV2] = + KnoraBaseToApiV2SimpleTransformationRules.externalClassesToAdd.view + .filterKeys(classIris) + .toMap ++ + KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd.view.filterKeys(classIris).toMap + + hardCodedExternalPropertiesAvailable: Map[SmartIri, ReadPropertyInfoV2] = + KnoraBaseToApiV2SimpleTransformationRules.externalPropertiesToAdd.view + .filterKeys(propertyIris) + .toMap ++ + KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd.view.filterKeys(propertyIris) + + // Convert the remaining external entity IRIs to internal ones. + + internalToExternalClassIris: Map[SmartIri, SmartIri] = + (classIris -- hardCodedExternalClassesAvailable.keySet) + .map(externalIri => externalIri.toOntologySchema(InternalSchema) -> externalIri) + .toMap + internalToExternalPropertyIris: Map[SmartIri, SmartIri] = + (propertyIris -- hardCodedExternalPropertiesAvailable.keySet) + .map(externalIri => externalIri.toOntologySchema(InternalSchema) -> externalIri) + .toMap + + classIrisForCache = internalToExternalClassIris.keySet + propertyIrisForCache = internalToExternalPropertyIris.keySet + + // Get the entities that are available in the ontology cache. + + classOntologiesForCache: Iterable[ReadOntologyV2] = cacheData.ontologies.view + .filterKeys(classIrisForCache.map(_.getOntologyFromEntity)) + .toMap + .values + propertyOntologiesForCache: Iterable[ReadOntologyV2] = + cacheData.ontologies.view + .filterKeys(propertyIrisForCache.map(_.getOntologyFromEntity)) + .toMap + .values + + classesAvailableFromCache: Map[SmartIri, ReadClassInfoV2] = classOntologiesForCache.flatMap { ontology => + ontology.classes.view + .filterKeys(classIrisForCache) + .toMap + }.toMap + + propertiesAvailableFromCache: Map[SmartIri, ReadPropertyInfoV2] = propertyOntologiesForCache.flatMap { ontology => + ontology.properties.view + .filterKeys(propertyIrisForCache) + .toMap + }.toMap + + allClassesAvailable: Map[SmartIri, ReadClassInfoV2] = + classesAvailableFromCache ++ hardCodedExternalClassesAvailable + allPropertiesAvailable: Map[SmartIri, ReadPropertyInfoV2] = + propertiesAvailableFromCache ++ hardCodedExternalPropertiesAvailable + + // See if any entities are missing. + + allExternalClassIrisAvailable: Set[SmartIri] = allClassesAvailable.keySet.map { classIri => + if (classIri.getOntologySchema.contains(InternalSchema)) { + internalToExternalClassIris(classIri) + } else { + classIri + } + } + + allExternalPropertyIrisAvailable = allPropertiesAvailable.keySet.map { propertyIri => + if (propertyIri.getOntologySchema.contains(InternalSchema)) { + internalToExternalPropertyIris(propertyIri) + } else { + propertyIri + } + } + + missingClasses = classIris -- allExternalClassIrisAvailable + missingProperties = propertyIris -- allExternalPropertyIrisAvailable + + missingEntities = missingClasses ++ missingProperties + + _ = if (missingEntities.nonEmpty) { + throw NotFoundException(s"Some requested entities were not found: ${missingEntities.mkString(", ")}") + } + + response = EntityInfoGetResponseV2( + classInfoMap = new ErrorHandlingMap(allClassesAvailable, key => s"Resource class $key not found"), + propertyInfoMap = new ErrorHandlingMap(allPropertiesAvailable, key => s"Property $key not found") + ) + } yield response + } + + /** + * Requests information about OWL classes in a single ontology. + * + * @param classIris the IRIs (internal or external) of the classes to query for. + * @param requestingUser the user making the request. + * @return a [[ReadOntologyV2]]. + */ + def getClassDefinitionsFromOntologyV2( + classIris: Set[SmartIri], + allLanguages: Boolean, + requestingUser: UserADM + )(implicit ec: ExecutionContext): Future[ReadOntologyV2] = + for { + cacheData <- Cache.getCacheData + + ontologyIris = classIris.map(_.getOntologyFromEntity) + + _ = if (ontologyIris.size != 1) { + throw BadRequestException(s"Only one ontology may be queried per request") + } + + classInfoResponse: EntityInfoGetResponseV2 <- + getEntityInfoResponseV2(classIris = classIris, requestingUser = requestingUser) + internalOntologyIri = ontologyIris.head.toOntologySchema(InternalSchema) + + // Are we returning data in the user's preferred language, or in all available languages? + userLang = if (!allLanguages) { + // Just the user's preferred language. + Some(requestingUser.lang) + } else { + // All available languages. + None + } + } yield ReadOntologyV2( + ontologyMetadata = cacheData.ontologies(internalOntologyIri).ontologyMetadata, + classes = classInfoResponse.classInfoMap, + userLang = userLang + ) + + /** + * Loads a class definition from the triplestore and converts it to a [[ClassInfoContentV2]]. + * + * @param classIri the IRI of the class to be loaded. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[ClassInfoContentV2]] representing the class definition. + */ + def loadClassDefinition( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + classIri: SmartIri, + featureFactoryConfig: FeatureFactoryConfig + )(implicit ex: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[ClassInfoContentV2] = + for { + sparql <- Future( + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .getClassDefinition( + triplestore = settings.triplestoreType, + classIri = classIri + ) + .toString() + ) + + constructResponse <- (storeManager ? SparqlExtendedConstructRequest( + sparql = sparql, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] + } yield constructResponseToClassDefinition( + classIri = classIri, + constructResponse = constructResponse + ) + + /** + * Extracts class definitions from a SPARQL CONSTRUCT response. + * + * @param classIris the IRIs of the classes to be read. + * @param constructResponse the SPARQL CONSTRUCT response to be read. + * @return a map of class IRIs to class definitions. + */ + def constructResponseToClassDefinitions(classIris: Set[SmartIri], constructResponse: SparqlExtendedConstructResponse)( + implicit stringFormatter: StringFormatter + ): Map[SmartIri, ClassInfoContentV2] = + classIris.map { classIri => + classIri -> constructResponseToClassDefinition( + classIri = classIri, + constructResponse = constructResponse + ) + }.toMap + + /** + * Converts a SPARQL CONSTRUCT response to a [[ClassInfoContentV2]]. + * + * @param classIri the IRI of the class to be read. + * @param constructResponse the SPARQL CONSTRUCT response to be read. + * @return a [[ClassInfoContentV2]] representing a class definition. + */ + private def constructResponseToClassDefinition( + classIri: SmartIri, + constructResponse: SparqlExtendedConstructResponse + )(implicit stringFormatter: StringFormatter): ClassInfoContentV2 = { + // All classes defined in the triplestore must be in Knora ontologies. + + val ontologyIri = classIri.getOntologyFromEntity + + if (!ontologyIri.isKnoraOntologyIri) { + throw InconsistentRepositoryDataException(s"Class $classIri is not in a Knora ontology") + } + + val statements = constructResponse.statements + + // Get the statements whose subject is the class. + val classDefMap: Map[SmartIri, Seq[LiteralV2]] = statements(IriSubjectV2(classIri.toString)) + + // Get the IRIs of the class's base classes. + + val subClassOfObjects: Seq[LiteralV2] = + classDefMap.getOrElse(OntologyConstants.Rdfs.SubClassOf.toSmartIri, Seq.empty[LiteralV2]) + + val subClassOf: Set[SmartIri] = subClassOfObjects.collect { case iriLiteral: IriLiteralV2 => + iriLiteral.value.toSmartIri + }.toSet + + // Get the blank nodes representing cardinalities. + + val restrictionBlankNodeIDs: Set[BlankNodeLiteralV2] = subClassOfObjects.collect { + case blankNodeLiteral: BlankNodeLiteralV2 => blankNodeLiteral + }.toSet + + val directCardinalities: Map[SmartIri, KnoraCardinalityInfo] = restrictionBlankNodeIDs.map { blankNodeID => + val blankNode: Map[SmartIri, Seq[LiteralV2]] = statements.getOrElse( + BlankNodeSubjectV2(blankNodeID.value), + throw InconsistentRepositoryDataException( + s"Blank node '${blankNodeID.value}' not found in construct query result" + ) + ) + + val blankNodeTypeObjs: Seq[LiteralV2] = blankNode.getOrElse( + OntologyConstants.Rdf.Type.toSmartIri, + throw InconsistentRepositoryDataException(s"Blank node '${blankNodeID.value}' has no rdf:type") + ) + + blankNodeTypeObjs match { + case Seq(IriLiteralV2(OntologyConstants.Owl.Restriction)) => () + case _ => + throw InconsistentRepositoryDataException(s"Blank node '${blankNodeID.value}' is not an owl:Restriction") + } + + val onPropertyObjs: Seq[LiteralV2] = blankNode.getOrElse( + OntologyConstants.Owl.OnProperty.toSmartIri, + throw InconsistentRepositoryDataException(s"Blank node '${blankNodeID.value}' has no owl:onProperty") + ) + + val propertyIri: SmartIri = onPropertyObjs match { + case Seq(propertyIri: IriLiteralV2) => propertyIri.value.toSmartIri + case other => throw InconsistentRepositoryDataException(s"Invalid object for owl:onProperty: $other") + } + + val owlCardinalityPreds: Set[SmartIri] = blankNode.keySet.filter { predicate => + OntologyConstants.Owl.cardinalityOWLRestrictions(predicate.toString) + } + + if (owlCardinalityPreds.size != 1) { + throw InconsistentRepositoryDataException( + s"Expected one cardinality predicate in blank node '${blankNodeID.value}', got ${owlCardinalityPreds.size}" + ) + } + + val owlCardinalityIri = owlCardinalityPreds.head + + val owlCardinalityValue: Int = blankNode(owlCardinalityIri) match { + case Seq(IntLiteralV2(intVal)) => intVal + case other => + throw InconsistentRepositoryDataException( + s"Expected one integer object for predicate $owlCardinalityIri in blank node '${blankNodeID.value}', got $other" + ) + } + + val guiOrder: Option[Int] = blankNode.get(OntologyConstants.SalsahGui.GuiOrder.toSmartIri) match { + case Some(Seq(IntLiteralV2(intVal))) => Some(intVal) + case None => None + case other => + throw InconsistentRepositoryDataException( + s"Expected one integer object for predicate ${OntologyConstants.SalsahGui.GuiOrder} in blank node '${blankNodeID.value}', got $other" + ) + } + + // salsah-gui:guiElement and salsah-gui:guiAttribute aren't allowed here. + + if (blankNode.contains(OntologyConstants.SalsahGui.GuiElementProp.toSmartIri)) { + throw InconsistentRepositoryDataException( + s"Class $classIri contains salsah-gui:guiElement in an owl:Restriction" + ) + } + + if (blankNode.contains(OntologyConstants.SalsahGui.GuiAttribute.toSmartIri)) { + throw InconsistentRepositoryDataException( + s"Class $classIri contains salsah-gui:guiAttribute in an owl:Restriction" + ) + } + + propertyIri -> Cardinality.owlCardinality2KnoraCardinality( + propertyIri = propertyIri.toString, + owlCardinality = Cardinality.OwlCardinalityInfo( + owlCardinalityIri = owlCardinalityIri.toString, + owlCardinalityValue = owlCardinalityValue, + guiOrder = guiOrder + ) + ) + }.toMap + + // Get any other predicates of the class. + + val otherPreds: Map[SmartIri, PredicateInfoV2] = getEntityPredicatesFromConstructResponse( + classDefMap - OntologyConstants.Rdfs.SubClassOf.toSmartIri + ) + + ClassInfoContentV2( + classIri = classIri, + subClassOf = subClassOf, + predicates = otherPreds, + directCardinalities = directCardinalities, + ontologySchema = classIri.getOntologySchema.get + ) + } + + /** + * Checks that the last modification date of an ontology is the same as the one we expect it to be. If not, return + * an error message fitting for the "before update" case. + * + * @param settings the application settings. + * @param storeManager the store manager actor ref. + * @param internalOntologyIri the internal IRI of the ontology. + * @param expectedLastModificationDate the last modification date that should now be attached to the ontology. + * @param featureFactoryConfig the feature factory configuration. + * @return a failed Future if the expected last modification date is not found. + */ + def checkOntologyLastModificationDateBeforeUpdate( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + internalOntologyIri: SmartIri, + expectedLastModificationDate: Instant, + featureFactoryConfig: FeatureFactoryConfig + )(implicit ec: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[Unit] = + checkOntologyLastModificationDate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = expectedLastModificationDate, + featureFactoryConfig = featureFactoryConfig, + errorFun = throw EditConflictException( + s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} has been modified by another user, please reload it and try again." + ) + ) + + /** + * Checks that the last modification date of an ontology is the same as the one we expect it to be. If not, return + * an error message fitting for the "after update" case. + * + * @param settings the application settings. + * @param storeManager the store manager actor ref. + * @param internalOntologyIri the internal IRI of the ontology. + * @param expectedLastModificationDate the last modification date that should now be attached to the ontology. + * @param featureFactoryConfig the feature factory configuration. + * @return a failed Future if the expected last modification date is not found. + */ + def checkOntologyLastModificationDateAfterUpdate( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + internalOntologyIri: SmartIri, + expectedLastModificationDate: Instant, + featureFactoryConfig: FeatureFactoryConfig + )(implicit ec: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[Unit] = + checkOntologyLastModificationDate( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = expectedLastModificationDate, + featureFactoryConfig = featureFactoryConfig, + errorFun = throw UpdateNotPerformedException( + s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} was not updated. Please report this as a possible bug." + ) + ) + + /** + * Checks that the last modification date of an ontology is the same as the one we expect it to be. + * + * @param settings the application settings. + * @param storeManager the store manager actor ref. + * @param internalOntologyIri the internal IRI of the ontology. + * @param expectedLastModificationDate the last modification date that the ontology is expected to have. + * @param featureFactoryConfig the feature factory configuration. + * @param errorFun a function that throws an exception. It will be called if the expected last modification date is not found. + * @return a failed Future if the expected last modification date is not found. + */ + private def checkOntologyLastModificationDate( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + internalOntologyIri: SmartIri, + expectedLastModificationDate: Instant, + featureFactoryConfig: FeatureFactoryConfig, + errorFun: => Nothing + )(implicit ec: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[Unit] = + for { + existingOntologyMetadata: Option[OntologyMetadataV2] <- loadOntologyMetadata( + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = featureFactoryConfig + ) + + _ = existingOntologyMetadata match { + case Some(metadata) => + metadata.lastModificationDate match { + case Some(lastModificationDate) => + if (lastModificationDate != expectedLastModificationDate) { + errorFun + } + + case None => + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has no ${OntologyConstants.KnoraBase.LastModificationDate}" + ) + } + + case None => + throw NotFoundException( + s"Ontology $internalOntologyIri (corresponding to ${internalOntologyIri.toOntologySchema(ApiV2Complex)}) not found" + ) + } + } yield () + + /** + * Checks whether the requesting user has permission to update an ontology. + * + * @param internalOntologyIri the internal IRI of the ontology. + * @param requestingUser the user making the request. + * @return `true` if the user has permission to update the ontology + */ + def canUserUpdateOntology(internalOntologyIri: SmartIri, requestingUser: UserADM)(implicit + ec: ExecutionContext + ): Future[Boolean] = + for { + cacheData <- Cache.getCacheData + + projectIri = + cacheData.ontologies + .getOrElse( + internalOntologyIri, + throw NotFoundException(s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} not found") + ) + .ontologyMetadata + .projectIri + .get + } yield requestingUser.permissions.isProjectAdmin(projectIri.toString) || requestingUser.permissions.isSystemAdmin + + /** + * Throws an exception if the requesting user does not have permission to update an ontology. + * + * @param internalOntologyIri the internal IRI of the ontology. + * @param requestingUser the user making the request. + * @return the project IRI. + */ + def checkPermissionsForOntologyUpdate(internalOntologyIri: SmartIri, requestingUser: UserADM)(implicit + ec: ExecutionContext + ): Future[SmartIri] = + for { + cacheData <- Cache.getCacheData + + projectIri = + cacheData.ontologies + .getOrElse( + internalOntologyIri, + throw NotFoundException(s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} not found") + ) + .ontologyMetadata + .projectIri + .get + + _ = if ( + !requestingUser.permissions.isProjectAdmin(projectIri.toString) && !requestingUser.permissions.isSystemAdmin + ) { + // not a project or system admin + throw ForbiddenException("Ontologies can be modified only by a project or system admin.") + } + + } yield projectIri + + /** + * Checks whether an ontology IRI is valid for an update. + * + * @param externalOntologyIri the external IRI of the ontology. + * @return a failed Future if the IRI is not valid for an update. + */ + def checkExternalOntologyIriForUpdate(externalOntologyIri: SmartIri): Future[Unit] = + if (!externalOntologyIri.isKnoraOntologyIri) { + FastFuture.failed(throw BadRequestException(s"Invalid ontology IRI for request: $externalOntologyIri}")) + } else if (!externalOntologyIri.getOntologySchema.contains(ApiV2Complex)) { + FastFuture.failed(throw BadRequestException(s"Invalid ontology schema for request: $externalOntologyIri")) + } else if (externalOntologyIri.isKnoraBuiltInDefinitionIri) { + FastFuture.failed( + throw BadRequestException(s"Ontology $externalOntologyIri cannot be modified via the Knora API") + ) + } else { + FastFuture.successful(()) + } + + /** + * Checks whether an entity IRI is valid for an update. + * + * @param externalEntityIri the external IRI of the entity. + * @return a failed Future if the entity IRI is not valid for an update, or is not from the specified ontology. + */ + private def checkExternalEntityIriForUpdate(externalEntityIri: SmartIri): Future[Unit] = + if (!externalEntityIri.isKnoraApiV2EntityIri) { + FastFuture.failed(throw BadRequestException(s"Invalid entity IRI for request: $externalEntityIri")) + } else if (!externalEntityIri.getOntologySchema.contains(ApiV2Complex)) { + FastFuture.failed(throw BadRequestException(s"Invalid ontology schema for request: $externalEntityIri")) + } else { + FastFuture.successful(()) + } + + /** + * Given the definition of a link property, returns the definition of the corresponding link value property. + * + * @param internalPropertyDef the definition of the the link property, in the internal schema. + * @return the definition of the corresponding link value property. + */ + def linkPropertyDefToLinkValuePropertyDef( + internalPropertyDef: PropertyInfoContentV2 + )(implicit stringFormatter: StringFormatter): PropertyInfoContentV2 = { + val linkValuePropIri = internalPropertyDef.propertyIri.fromLinkPropToLinkValueProp + + val newPredicates: Map[SmartIri, PredicateInfoV2] = + (internalPropertyDef.predicates - OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri) + + (OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, + objects = Seq(SmartIriLiteralV2(OntologyConstants.KnoraBase.LinkValue.toSmartIri)) + )) + + internalPropertyDef.copy( + propertyIri = linkValuePropIri, + predicates = newPredicates, + subPropertyOf = Set(OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri) + ) + } + + /** + * Given the cardinalities directly defined on a given class, and the cardinalities that it could inherit (directly + * or indirectly) from its base classes, combines the two, filtering out the base class cardinalities ones that are overridden + * by cardinalities defined directly on the given class. Checks that if a directly defined cardinality overrides an inheritable one, + * the directly defined one is at least as restrictive as the inheritable one. + * + * @param classIri the class IRI. + * @param thisClassCardinalities the cardinalities directly defined on a given resource class. + * @param inheritableCardinalities the cardinalities that the given resource class could inherit from its base classes. + * @param allSubPropertyOfRelations a map in which each property IRI points to the full set of its base properties. + * @param errorSchema the ontology schema to be used in error messages. + * @param errorFun a function that throws an exception. It will be called with an error message argument if the cardinalities are invalid. + * @return a map in which each key is the IRI of a property that has a cardinality in the resource class (or that it inherits + * from its base classes), and each value is the cardinality on the property. + */ + def overrideCardinalities( + classIri: SmartIri, + thisClassCardinalities: Map[SmartIri, KnoraCardinalityInfo], + inheritableCardinalities: Map[SmartIri, KnoraCardinalityInfo], + allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], + errorSchema: OntologySchema, + errorFun: String => Nothing + ): Map[SmartIri, KnoraCardinalityInfo] = { + // A map of directly defined properties to the base class properties they can override. + val overrides: Map[SmartIri, Set[SmartIri]] = thisClassCardinalities.map { + case (thisClassProp, thisClassCardinality) => + // For each directly defined cardinality, get its base properties, if available. + // If the class has a cardinality for a non-Knora property like rdfs:label (which can happen only + // if it's a built-in class), we won't have any information about the base properties of that property. + val basePropsOfThisClassProp: Set[SmartIri] = + allSubPropertyOfRelations.getOrElse(thisClassProp, Set.empty[SmartIri]) + + val overriddenBaseProps: Set[SmartIri] = inheritableCardinalities.foldLeft(Set.empty[SmartIri]) { + case (acc, (baseClassProp, baseClassCardinality)) => + // Can the directly defined cardinality override the inheritable one? + if (thisClassProp == baseClassProp || basePropsOfThisClassProp.contains(baseClassProp)) { + // Yes. Is the directly defined one at least as restrictive as the inheritable one? + + if ( + !Cardinality.isCompatible( + directCardinality = thisClassCardinality.cardinality, + inheritableCardinality = baseClassCardinality.cardinality + ) + ) { + // No. Throw an exception. + errorFun( + s"In class <${classIri.toOntologySchema(errorSchema)}>, the directly defined cardinality ${thisClassCardinality.cardinality} on ${thisClassProp + .toOntologySchema(errorSchema)} is not compatible with the inherited cardinality ${baseClassCardinality.cardinality} on ${baseClassProp + .toOntologySchema(errorSchema)}, because it is less restrictive" + ) + } else { + // Yes. Filter out the inheritable one, because the directly defined one overrides it. + acc + baseClassProp + } + } else { + // No. Let the class inherit the inheritable cardinality. + acc + } + } + + thisClassProp -> overriddenBaseProps + } + + // A map of base class properties to the directly defined properties that can override them. + val reverseOverrides: Map[SmartIri, Set[SmartIri]] = overrides.toVector.flatMap { + // Unpack the sets to make an association list. + case (thisClassProp: SmartIri, baseClassProps: Set[SmartIri]) => + baseClassProps.map { baseClassProp: SmartIri => + thisClassProp -> baseClassProp + } + }.map { + // Reverse the direction of the association list. + case (thisClassProp: SmartIri, baseClassProp: SmartIri) => + baseClassProp -> thisClassProp + }.groupBy { + // Group by base class prop to make a map. + case (baseClassProp: SmartIri, _) => baseClassProp + }.map { + // Make sets of directly defined props. + case (baseClassProp: SmartIri, thisClassProps: immutable.Iterable[(SmartIri, SmartIri)]) => + baseClassProp -> thisClassProps.map { case (_, thisClassProp) => + thisClassProp + }.toSet + } + + // Are there any base class properties that are overridden by more than one directly defined property, + // and do any of those base properties have cardinalities that could cause conflicts between the cardinalities + // on the directly defined cardinalities? + reverseOverrides.foreach { case (baseClassProp, thisClassProps) => + if (thisClassProps.size > 1) { + val overriddenCardinality: KnoraCardinalityInfo = inheritableCardinalities(baseClassProp) + + if ( + overriddenCardinality.cardinality == Cardinality.MustHaveOne || overriddenCardinality.cardinality == Cardinality.MayHaveOne + ) { + errorFun( + s"In class <${classIri.toOntologySchema(errorSchema)}>, there is more than one cardinality that would override the inherited cardinality $overriddenCardinality on <${baseClassProp + .toOntologySchema(errorSchema)}>" + ) + } + } + } + + thisClassCardinalities ++ inheritableCardinalities.filterNot { case (basePropIri, _) => + reverseOverrides.contains(basePropIri) + } + } + + /** + * Given all the `rdfs:subClassOf` relations between classes, calculates all the inverse relations. + * + * @param allSubClassOfRelations all the `rdfs:subClassOf` relations between classes. + * @return a map of IRIs of resource classes to sets of the IRIs of their subclasses. + */ + def calculateSuperClassOfRelations( + allSubClassOfRelations: Map[SmartIri, Seq[SmartIri]] + ): Map[SmartIri, Set[SmartIri]] = + allSubClassOfRelations.toVector.flatMap { case (subClass: SmartIri, baseClasses: Seq[SmartIri]) => + baseClasses.map { baseClass => + baseClass -> subClass + } + } + .groupBy(_._1) + .map { case (baseClass: SmartIri, baseClassAndSubClasses: Vector[(SmartIri, SmartIri)]) => + baseClass -> baseClassAndSubClasses.map(_._2).toSet + } + + /** + * Given a class loaded from the triplestore, recursively adds its inherited cardinalities to the cardinalities it defines + * directly. A cardinality for a subproperty in a subclass overrides a cardinality for a base property in + * a base class. + * + * @param classIri the IRI of the class whose properties are to be computed. + * @param directSubClassOfRelations a map of the direct `rdfs:subClassOf` relations defined on each class. + * @param allSubPropertyOfRelations a map in which each property IRI points to the full set of its base properties. + * @param directClassCardinalities a map of the cardinalities defined directly on each class. + * @return a map in which each key is the IRI of a property that has a cardinality in the class (or that it inherits + * from its base classes), and each value is the cardinality on the property. + */ + def inheritCardinalitiesInLoadedClass( + classIri: SmartIri, + directSubClassOfRelations: Map[SmartIri, Set[SmartIri]], + allSubPropertyOfRelations: Map[SmartIri, Set[SmartIri]], + directClassCardinalities: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] + ): Map[SmartIri, KnoraCardinalityInfo] = { + // Recursively get properties that are available to inherit from base classes. If we have no information about + // a class, that could mean that it isn't a subclass of knora-base:Resource (e.g. it's something like + // foaf:Person), in which case we assume that it has no base classes. + val cardinalitiesAvailableToInherit: Map[SmartIri, KnoraCardinalityInfo] = directSubClassOfRelations + .getOrElse(classIri, Set.empty[SmartIri]) + .foldLeft(Map.empty[SmartIri, KnoraCardinalityInfo]) { + case (acc: Map[SmartIri, KnoraCardinalityInfo], baseClass: SmartIri) => + acc ++ inheritCardinalitiesInLoadedClass( + classIri = baseClass, + directSubClassOfRelations = directSubClassOfRelations, + allSubPropertyOfRelations = allSubPropertyOfRelations, + directClassCardinalities = directClassCardinalities + ) + } + + // Get the properties that have cardinalities defined directly on this class. Again, if we have no information + // about a class, we assume that it has no cardinalities. + val thisClassCardinalities: Map[SmartIri, KnoraCardinalityInfo] = + directClassCardinalities.getOrElse(classIri, Map.empty[SmartIri, KnoraCardinalityInfo]) + + // Combine the cardinalities defined directly on this class with the ones that are available to inherit. + overrideCardinalities( + classIri = classIri, + thisClassCardinalities = thisClassCardinalities, + inheritableCardinalities = cardinalitiesAvailableToInherit, + allSubPropertyOfRelations = allSubPropertyOfRelations, + errorSchema = InternalSchema, + { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) + } + + /** + * Checks whether a class IRI refers to a Knora internal resource class. + * + * @param classIri the class IRI. + * @return `true` if the class IRI refers to a Knora resource class, or `false` if the class + * does not exist or is not a Knora internal resource class. + */ + def isKnoraInternalResourceClass(classIri: SmartIri, cacheData: OntologyCacheData): Boolean = + classIri.isKnoraInternalEntityIri && + cacheData.ontologies(classIri.getOntologyFromEntity).classes.get(classIri).exists(_.isResourceClass) + + /** + * Checks whether a property is a subproperty of `knora-base:resourceProperty`. + * + * @param propertyIri the property IRI. + * @param cacheData the ontology cache. + * @return `true` if the property is a subproperty of `knora-base:resourceProperty`. + */ + def isKnoraResourceProperty(propertyIri: SmartIri, cacheData: OntologyCacheData): Boolean = + propertyIri.isKnoraEntityIri && + cacheData.ontologies(propertyIri.getOntologyFromEntity).properties.get(propertyIri).exists(_.isResourceProp) + + /** + * Checks whether a property is a subproperty of `knora-base:hasLinkTo`. + * + * @param propertyIri the property IRI. + * @param cacheData the ontology cache. + * @return `true` if the property is a subproperty of `knora-base:hasLinkTo`. + */ + def isLinkProp(propertyIri: SmartIri, cacheData: OntologyCacheData): Boolean = + propertyIri.isKnoraEntityIri && + cacheData.ontologies(propertyIri.getOntologyFromEntity).properties.get(propertyIri).exists(_.isLinkProp) + + /** + * Checks whether a property is a subproperty of `knora-base:hasLinkToValue`. + * + * @param propertyIri the property IRI. + * @param cacheData the ontology cache. + * @return `true` if the property is a subproperty of `knora-base:hasLinkToValue`. + */ + def isLinkValueProp(propertyIri: SmartIri, cacheData: OntologyCacheData): Boolean = + propertyIri.isKnoraEntityIri && + cacheData.ontologies(propertyIri.getOntologyFromEntity).properties.get(propertyIri).exists(_.isLinkValueProp) + + /** + * Checks whether a property is a subproperty of `knora-base:hasFileValue`. + * + * @param propertyIri the property IRI. + * @param cacheData the ontology cache. + * @return `true` if the property is a subproperty of `knora-base:hasFileValue`. + */ + def isFileValueProp(propertyIri: SmartIri, cacheData: OntologyCacheData): Boolean = + propertyIri.isKnoraEntityIri && + cacheData.ontologies(propertyIri.getOntologyFromEntity).properties.get(propertyIri).exists(_.isFileValueProp) + +} diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala index 781685f6a7..ea05706df0 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala @@ -39,18 +39,18 @@ object OntologiesRouteV2 { } /** - * Provides a routing function for API v2 routes that deal with ontologies. - */ + * Provides a routing function for API v2 routes that deal with ontologies. + */ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { import OntologiesRouteV2._ - private val ALL_LANGUAGES = "allLanguages" + private val ALL_LANGUAGES = "allLanguages" private val LAST_MODIFICATION_DATE = "lastModificationDate" /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = dereferenceOntologyIri(featureFactoryConfig) ~ getOntologyMetadata(featureFactoryConfig) ~ @@ -62,6 +62,8 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) addCardinalities(featureFactoryConfig) ~ canReplaceCardinalities(featureFactoryConfig) ~ replaceCardinalities(featureFactoryConfig) ~ + canDeleteCardinalitiesFromClass(featureFactoryConfig) ~ + deleteCardinalitiesFromClass(featureFactoryConfig) ~ changeGuiOrder(featureFactoryConfig) ~ getClasses(featureFactoryConfig) ~ canDeleteClass(featureFactoryConfig) ~ @@ -80,90 +82,89 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private def dereferenceOntologyIri(featureFactoryConfig: FeatureFactoryConfig): Route = path("ontology" / Segments) { _: List[String] => get { requestContext => - { - // This is the route used to dereference an actual ontology IRI. If the URL path looks like it - // belongs to a built-in API ontology (which has to contain "knora-api"), prefix it with - // http://api.knora.org to get the ontology IRI. Otherwise, if it looks like it belongs to a - // project-specific API ontology, prefix it with settings.externalOntologyIriHostAndPort to get the - // ontology IRI. - - val urlPath = requestContext.request.uri.path.toString - - val requestedOntologyStr: IRI = if (stringFormatter.isBuiltInApiV2OntologyUrlPath(urlPath)) { - OntologyConstants.KnoraApi.ApiOntologyHostname + urlPath - } else if (stringFormatter.isProjectSpecificApiV2OntologyUrlPath(urlPath)) { - "http://" + settings.externalOntologyIriHostAndPort + urlPath - } else { - throw BadRequestException(s"Invalid or unknown URL path for external ontology: $urlPath") - } + // This is the route used to dereference an actual ontology IRI. If the URL path looks like it + // belongs to a built-in API ontology (which has to contain "knora-api"), prefix it with + // http://api.knora.org to get the ontology IRI. Otherwise, if it looks like it belongs to a + // project-specific API ontology, prefix it with settings.externalOntologyIriHostAndPort to get the + // ontology IRI. + + val urlPath = requestContext.request.uri.path.toString + + val requestedOntologyStr: IRI = if (stringFormatter.isBuiltInApiV2OntologyUrlPath(urlPath)) { + OntologyConstants.KnoraApi.ApiOntologyHostname + urlPath + } else if (stringFormatter.isProjectSpecificApiV2OntologyUrlPath(urlPath)) { + "http://" + settings.externalOntologyIriHostAndPort + urlPath + } else { + throw BadRequestException(s"Invalid or unknown URL path for external ontology: $urlPath") + } - val requestedOntology = requestedOntologyStr.toSmartIriWithErr( - throw BadRequestException(s"Invalid ontology IRI: $requestedOntologyStr")) + val requestedOntology = requestedOntologyStr.toSmartIriWithErr( + throw BadRequestException(s"Invalid ontology IRI: $requestedOntologyStr") + ) - stringFormatter.checkExternalOntologyName(requestedOntology) + stringFormatter.checkExternalOntologyName(requestedOntology) - val targetSchema = requestedOntology.getOntologySchema match { - case Some(apiV2Schema: ApiV2Schema) => apiV2Schema - case _ => throw BadRequestException(s"Invalid ontology IRI: $requestedOntologyStr") - } + val targetSchema = requestedOntology.getOntologySchema match { + case Some(apiV2Schema: ApiV2Schema) => apiV2Schema + case _ => throw BadRequestException(s"Invalid ontology IRI: $requestedOntologyStr") + } - val params: Map[String, String] = requestContext.request.uri.query().toMap - val allLanguagesStr = params.get(ALL_LANGUAGES) - val allLanguages = stringFormatter.optionStringToBoolean( - allLanguagesStr, - throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr")) + val params: Map[String, String] = requestContext.request.uri.query().toMap + val allLanguagesStr = params.get(ALL_LANGUAGES) + val allLanguages = stringFormatter.optionStringToBoolean( + allLanguagesStr, + throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr") + ) - val requestMessageFuture: Future[OntologyEntitiesGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - OntologyEntitiesGetRequestV2( - ontologyIri = requestedOntology, - allLanguages = allLanguages, - requestingUser = requestingUser - ) + val requestMessageFuture: Future[OntologyEntitiesGetRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield OntologyEntitiesGetRequestV2( + ontologyIri = requestedOntology, + allLanguages = allLanguages, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = targetSchema, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = targetSchema, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } private def getOntologyMetadata(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "metadata") { get { requestContext => - { - val maybeProjectIri: Option[SmartIri] = RouteUtilV2.getProject(requestContext) + val maybeProjectIri: Option[SmartIri] = RouteUtilV2.getProject(requestContext) - val requestMessageFuture: Future[OntologyMetadataGetByProjectRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - OntologyMetadataGetByProjectRequestV2(projectIris = maybeProjectIri.toSet, requestingUser = requestingUser) + val requestMessageFuture: Future[OntologyMetadataGetByProjectRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield OntologyMetadataGetByProjectRequestV2( + projectIris = maybeProjectIri.toSet, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } @@ -176,20 +177,20 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[ChangeOntologyMetadataRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestMessage: ChangeOntologyMetadataRequestV2 <- ChangeOntologyMetadataRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -210,77 +211,76 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private def getOntologyMetadataForProjects(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "metadata" / Segments) { projectIris: List[IRI] => get { requestContext => - { - - val requestMessageFuture: Future[OntologyMetadataGetByProjectRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + val requestMessageFuture: Future[OntologyMetadataGetByProjectRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) - validatedProjectIris = projectIris + validatedProjectIris = + projectIris .map(iri => iri.toSmartIriWithErr(throw BadRequestException(s"Invalid project IRI: $iri"))) .toSet - } yield - OntologyMetadataGetByProjectRequestV2(projectIris = validatedProjectIris, requestingUser = requestingUser) + } yield OntologyMetadataGetByProjectRequestV2( + projectIris = validatedProjectIris, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } private def getOntology(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "allentities" / Segment) { externalOntologyIriStr: IRI => get { requestContext => - { - val requestedOntologyIri = externalOntologyIriStr.toSmartIriWithErr( - throw BadRequestException(s"Invalid ontology IRI: $externalOntologyIriStr")) + val requestedOntologyIri = externalOntologyIriStr.toSmartIriWithErr( + throw BadRequestException(s"Invalid ontology IRI: $externalOntologyIriStr") + ) - stringFormatter.checkExternalOntologyName(requestedOntologyIri) + stringFormatter.checkExternalOntologyName(requestedOntologyIri) - val targetSchema = requestedOntologyIri.getOntologySchema match { - case Some(apiV2Schema: ApiV2Schema) => apiV2Schema - case _ => throw BadRequestException(s"Invalid ontology IRI: $externalOntologyIriStr") - } + val targetSchema = requestedOntologyIri.getOntologySchema match { + case Some(apiV2Schema: ApiV2Schema) => apiV2Schema + case _ => throw BadRequestException(s"Invalid ontology IRI: $externalOntologyIriStr") + } - val params: Map[String, String] = requestContext.request.uri.query().toMap - val allLanguagesStr = params.get(ALL_LANGUAGES) - val allLanguages = stringFormatter.optionStringToBoolean( - params.get(ALL_LANGUAGES), - throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr")) + val params: Map[String, String] = requestContext.request.uri.query().toMap + val allLanguagesStr = params.get(ALL_LANGUAGES) + val allLanguages = stringFormatter.optionStringToBoolean( + params.get(ALL_LANGUAGES), + throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr") + ) - val requestMessageFuture: Future[OntologyEntitiesGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - OntologyEntitiesGetRequestV2( - ontologyIri = requestedOntologyIri, - allLanguages = allLanguages, - requestingUser = requestingUser - ) + val requestMessageFuture: Future[OntologyEntitiesGetRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield OntologyEntitiesGetRequestV2( + ontologyIri = requestedOntologyIri, + allLanguages = allLanguages, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = targetSchema, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = targetSchema, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } @@ -291,22 +291,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[CreateClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: CreateClassRequestV2 <- CreateClassRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -331,22 +331,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangeClassLabelsOrCommentsRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage <- ChangeClassLabelsOrCommentsRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -372,22 +372,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[AddCardinalitiesToClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: AddCardinalitiesToClassRequestV2 <- AddCardinalitiesToClassRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -417,15 +417,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[CanChangeCardinalitiesRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - CanChangeCardinalitiesRequestV2( - classIri = classIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield CanChangeCardinalitiesRequestV2( + classIri = classIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessageFuture, @@ -440,30 +439,114 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } + // Replaces all cardinalities with what was sent. Deleting means send empty + // replace request. private def replaceCardinalities(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "cardinalities") { put { - // Change a class's cardinalities. entity(as[String]) { jsonRequest => requestContext => { val requestMessageFuture: Future[ChangeCardinalitiesRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: ChangeCardinalitiesRequestV2 <- ChangeCardinalitiesRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) + } yield requestMessage + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) + } + } + } + } + + private def canDeleteCardinalitiesFromClass(featureFactoryConfig: FeatureFactoryConfig): Route = + path(OntologiesBasePath / "candeletecardinalities") { + post { + entity(as[String]) { jsonRequest => requestContext => + { + val requestMessageFuture: Future[CanDeleteCardinalitiesFromClassRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + + requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) + + requestMessage: CanDeleteCardinalitiesFromClassRequestV2 <- + CanDeleteCardinalitiesFromClassRequestV2.fromJsonLD( + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) + } yield requestMessage + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) + } + } + } + } + + // delete a single cardinality from the specified class if the property is + // not used in resources. + private def deleteCardinalitiesFromClass(featureFactoryConfig: FeatureFactoryConfig): Route = + path(OntologiesBasePath / "cardinalities") { + delete { + entity(as[String]) { jsonRequest => requestContext => + { + val requestMessageFuture: Future[DeleteCardinalitiesFromClassRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + + requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) + + requestMessage: DeleteCardinalitiesFromClassRequestV2 <- DeleteCardinalitiesFromClassRequestV2.fromJsonLD( + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -489,22 +572,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangeGuiOrderRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: ChangeGuiOrderRequestV2 <- ChangeGuiOrderRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -525,68 +608,66 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private def getClasses(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "classes" / Segments) { externalResourceClassIris: List[IRI] => get { requestContext => - { - val classesAndSchemas: Set[(SmartIri, ApiV2Schema)] = externalResourceClassIris.map { classIriStr: IRI => - val requestedClassIri: SmartIri = - classIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid class IRI: $classIriStr")) + val classesAndSchemas: Set[(SmartIri, ApiV2Schema)] = externalResourceClassIris.map { classIriStr: IRI => + val requestedClassIri: SmartIri = + classIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid class IRI: $classIriStr")) - stringFormatter.checkExternalOntologyName(requestedClassIri) + stringFormatter.checkExternalOntologyName(requestedClassIri) - if (!requestedClassIri.isKnoraApiV2EntityIri) { - throw BadRequestException(s"Invalid class IRI: $classIriStr") - } + if (!requestedClassIri.isKnoraApiV2EntityIri) { + throw BadRequestException(s"Invalid class IRI: $classIriStr") + } - val schema = requestedClassIri.getOntologySchema match { - case Some(apiV2Schema: ApiV2Schema) => apiV2Schema - case _ => throw BadRequestException(s"Invalid class IRI: $classIriStr") - } + val schema = requestedClassIri.getOntologySchema match { + case Some(apiV2Schema: ApiV2Schema) => apiV2Schema + case _ => throw BadRequestException(s"Invalid class IRI: $classIriStr") + } - (requestedClassIri, schema) - }.toSet + (requestedClassIri, schema) + }.toSet - val (classesForResponder: Set[SmartIri], schemas: Set[ApiV2Schema]) = classesAndSchemas.unzip + val (classesForResponder: Set[SmartIri], schemas: Set[ApiV2Schema]) = classesAndSchemas.unzip - if (classesForResponder.map(_.getOntologyFromEntity).size != 1) { - throw BadRequestException(s"Only one ontology may be queried per request") - } + if (classesForResponder.map(_.getOntologyFromEntity).size != 1) { + throw BadRequestException(s"Only one ontology may be queried per request") + } - // Decide which API schema to use for the response. - val targetSchema = if (schemas.size == 1) { - schemas.head - } else { - // The client requested different schemas. - throw BadRequestException("The request refers to multiple API schemas") - } + // Decide which API schema to use for the response. + val targetSchema = if (schemas.size == 1) { + schemas.head + } else { + // The client requested different schemas. + throw BadRequestException("The request refers to multiple API schemas") + } - val params: Map[String, String] = requestContext.request.uri.query().toMap - val allLanguagesStr = params.get(ALL_LANGUAGES) - val allLanguages = stringFormatter.optionStringToBoolean( - params.get(ALL_LANGUAGES), - throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr")) + val params: Map[String, String] = requestContext.request.uri.query().toMap + val allLanguagesStr = params.get(ALL_LANGUAGES) + val allLanguages = stringFormatter.optionStringToBoolean( + params.get(ALL_LANGUAGES), + throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr") + ) - val requestMessageFuture: Future[ClassesGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - ClassesGetRequestV2( - classIris = classesForResponder, - allLanguages = allLanguages, - requestingUser = requestingUser - ) + val requestMessageFuture: Future[ClassesGetRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield ClassesGetRequestV2( + classIris = classesForResponder, + allLanguages = allLanguages, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = targetSchema, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = targetSchema, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } @@ -602,15 +683,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[CanDeleteClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - CanDeleteClassRequestV2( - classIri = classIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield CanDeleteClassRequestV2( + classIri = classIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessageFuture, @@ -628,101 +708,95 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private def deleteClass(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "classes" / Segments) { externalResourceClassIris: List[IRI] => delete { requestContext => - { - - val classIriStr = externalResourceClassIris match { - case List(str) => str - case _ => throw BadRequestException(s"Only one class can be deleted at a time") - } + val classIriStr = externalResourceClassIris match { + case List(str) => str + case _ => throw BadRequestException(s"Only one class can be deleted at a time") + } - val classIri = classIriStr.toSmartIri - stringFormatter.checkExternalOntologyName(classIri) + val classIri = classIriStr.toSmartIri + stringFormatter.checkExternalOntologyName(classIri) - if (!classIri.getOntologySchema.contains(ApiV2Complex)) { - throw BadRequestException(s"Invalid class IRI for request: $classIriStr") - } + if (!classIri.getOntologySchema.contains(ApiV2Complex)) { + throw BadRequestException(s"Invalid class IRI for request: $classIriStr") + } - val lastModificationDateStr = requestContext.request.uri - .query() - .toMap - .getOrElse(LAST_MODIFICATION_DATE, throw BadRequestException(s"Missing parameter: $LAST_MODIFICATION_DATE")) - val lastModificationDate = stringFormatter.xsdDateTimeStampToInstant( - lastModificationDateStr, - throw BadRequestException(s"Invalid timestamp: $lastModificationDateStr")) + val lastModificationDateStr = requestContext.request.uri + .query() + .toMap + .getOrElse(LAST_MODIFICATION_DATE, throw BadRequestException(s"Missing parameter: $LAST_MODIFICATION_DATE")) + val lastModificationDate = stringFormatter.xsdDateTimeStampToInstant( + lastModificationDateStr, + throw BadRequestException(s"Invalid timestamp: $lastModificationDateStr") + ) - val requestMessageFuture: Future[DeleteClassRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - DeleteClassRequestV2( - classIri = classIri, - lastModificationDate = lastModificationDate, - apiRequestID = UUID.randomUUID, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val requestMessageFuture: Future[DeleteClassRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield DeleteClassRequestV2( + classIri = classIri, + lastModificationDate = lastModificationDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } private def deleteOntologyComment(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "comment" / Segment) { ontologyIriStr: IRI => delete { requestContext => - { - - val ontologyIri = ontologyIriStr.toSmartIri + val ontologyIri = ontologyIriStr.toSmartIri - if (!ontologyIri.getOntologySchema.contains(ApiV2Complex)) { - throw BadRequestException(s"Invalid class IRI for request: $ontologyIriStr") - } + if (!ontologyIri.getOntologySchema.contains(ApiV2Complex)) { + throw BadRequestException(s"Invalid class IRI for request: $ontologyIriStr") + } - val lastModificationDateStr = requestContext.request.uri - .query() - .toMap - .getOrElse(LAST_MODIFICATION_DATE, throw BadRequestException(s"Missing parameter: $LAST_MODIFICATION_DATE")) + val lastModificationDateStr = requestContext.request.uri + .query() + .toMap + .getOrElse(LAST_MODIFICATION_DATE, throw BadRequestException(s"Missing parameter: $LAST_MODIFICATION_DATE")) - val lastModificationDate = stringFormatter.xsdDateTimeStampToInstant( - lastModificationDateStr, - throw BadRequestException(s"Invalid timestamp: $lastModificationDateStr")) + val lastModificationDate = stringFormatter.xsdDateTimeStampToInstant( + lastModificationDateStr, + throw BadRequestException(s"Invalid timestamp: $lastModificationDateStr") + ) - val requestMessageFuture: Future[DeleteOntologyCommentRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - DeleteOntologyCommentRequestV2( - ontologyIri = ontologyIri, - lastModificationDate = lastModificationDate, - apiRequestID = UUID.randomUUID, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val requestMessageFuture: Future[DeleteOntologyCommentRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield DeleteOntologyCommentRequestV2( + ontologyIri = ontologyIri, + lastModificationDate = lastModificationDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } @@ -734,22 +808,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[CreatePropertyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: CreatePropertyRequestV2 <- CreatePropertyRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -775,23 +849,24 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangePropertyLabelsOrCommentsRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: ChangePropertyLabelsOrCommentsRequestV2 <- ChangePropertyLabelsOrCommentsRequestV2 - .fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + .fromJsonLD( + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = + featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -817,23 +892,23 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangePropertyGuiElementRequest] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: ChangePropertyGuiElementRequest <- ChangePropertyGuiElementRequest - .fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + .fromJsonLD( + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -854,68 +929,66 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private def getProperties(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "properties" / Segments) { externalPropertyIris: List[IRI] => get { requestContext => - { - val propsAndSchemas: Set[(SmartIri, ApiV2Schema)] = externalPropertyIris.map { propIriStr: IRI => - val requestedPropIri: SmartIri = - propIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid property IRI: $propIriStr")) + val propsAndSchemas: Set[(SmartIri, ApiV2Schema)] = externalPropertyIris.map { propIriStr: IRI => + val requestedPropIri: SmartIri = + propIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid property IRI: $propIriStr")) - stringFormatter.checkExternalOntologyName(requestedPropIri) + stringFormatter.checkExternalOntologyName(requestedPropIri) - if (!requestedPropIri.isKnoraApiV2EntityIri) { - throw BadRequestException(s"Invalid property IRI: $propIriStr") - } + if (!requestedPropIri.isKnoraApiV2EntityIri) { + throw BadRequestException(s"Invalid property IRI: $propIriStr") + } - val schema = requestedPropIri.getOntologySchema match { - case Some(apiV2Schema: ApiV2Schema) => apiV2Schema - case _ => throw BadRequestException(s"Invalid property IRI: $propIriStr") - } + val schema = requestedPropIri.getOntologySchema match { + case Some(apiV2Schema: ApiV2Schema) => apiV2Schema + case _ => throw BadRequestException(s"Invalid property IRI: $propIriStr") + } - (requestedPropIri, schema) - }.toSet + (requestedPropIri, schema) + }.toSet - val (propsForResponder: Set[SmartIri], schemas: Set[ApiV2Schema]) = propsAndSchemas.unzip + val (propsForResponder: Set[SmartIri], schemas: Set[ApiV2Schema]) = propsAndSchemas.unzip - if (propsForResponder.map(_.getOntologyFromEntity).size != 1) { - throw BadRequestException(s"Only one ontology may be queried per request") - } + if (propsForResponder.map(_.getOntologyFromEntity).size != 1) { + throw BadRequestException(s"Only one ontology may be queried per request") + } - // Decide which API schema to use for the response. - val targetSchema = if (schemas.size == 1) { - schemas.head - } else { - // The client requested different schemas. - throw BadRequestException("The request refers to multiple API schemas") - } + // Decide which API schema to use for the response. + val targetSchema = if (schemas.size == 1) { + schemas.head + } else { + // The client requested different schemas. + throw BadRequestException("The request refers to multiple API schemas") + } - val params: Map[String, String] = requestContext.request.uri.query().toMap - val allLanguagesStr = params.get(ALL_LANGUAGES) - val allLanguages = stringFormatter.optionStringToBoolean( - params.get(ALL_LANGUAGES), - throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr")) + val params: Map[String, String] = requestContext.request.uri.query().toMap + val allLanguagesStr = params.get(ALL_LANGUAGES) + val allLanguages = stringFormatter.optionStringToBoolean( + params.get(ALL_LANGUAGES), + throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr") + ) - val requestMessageFuture: Future[PropertiesGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - PropertiesGetRequestV2( - propertyIris = propsForResponder, - allLanguages = allLanguages, - requestingUser = requestingUser - ) + val requestMessageFuture: Future[PropertiesGetRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield PropertiesGetRequestV2( + propertyIris = propsForResponder, + allLanguages = allLanguages, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = targetSchema, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = targetSchema, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } @@ -931,15 +1004,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[CanDeletePropertyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - CanDeletePropertyRequestV2( - propertyIri = propertyIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield CanDeletePropertyRequestV2( + propertyIri = propertyIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessageFuture, @@ -957,52 +1029,50 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private def deleteProperty(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / "properties" / Segments) { externalPropertyIris: List[IRI] => delete { requestContext => - { - val propertyIriStr = externalPropertyIris match { - case List(str) => str - case _ => throw BadRequestException(s"Only one property can be deleted at a time") - } + val propertyIriStr = externalPropertyIris match { + case List(str) => str + case _ => throw BadRequestException(s"Only one property can be deleted at a time") + } - val propertyIri = propertyIriStr.toSmartIri - stringFormatter.checkExternalOntologyName(propertyIri) + val propertyIri = propertyIriStr.toSmartIri + stringFormatter.checkExternalOntologyName(propertyIri) - if (!propertyIri.getOntologySchema.contains(ApiV2Complex)) { - throw BadRequestException(s"Invalid property IRI for request: $propertyIri") - } + if (!propertyIri.getOntologySchema.contains(ApiV2Complex)) { + throw BadRequestException(s"Invalid property IRI for request: $propertyIri") + } - val lastModificationDateStr = requestContext.request.uri - .query() - .toMap - .getOrElse(LAST_MODIFICATION_DATE, throw BadRequestException(s"Missing parameter: $LAST_MODIFICATION_DATE")) - val lastModificationDate = stringFormatter.xsdDateTimeStampToInstant( - lastModificationDateStr, - throw BadRequestException(s"Invalid timestamp: $lastModificationDateStr")) + val lastModificationDateStr = requestContext.request.uri + .query() + .toMap + .getOrElse(LAST_MODIFICATION_DATE, throw BadRequestException(s"Missing parameter: $LAST_MODIFICATION_DATE")) + val lastModificationDate = stringFormatter.xsdDateTimeStampToInstant( + lastModificationDateStr, + throw BadRequestException(s"Invalid timestamp: $lastModificationDateStr") + ) - val requestMessageFuture: Future[DeletePropertyRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - DeletePropertyRequestV2( - propertyIri = propertyIri, - lastModificationDate = lastModificationDate, - apiRequestID = UUID.randomUUID, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val requestMessageFuture: Future[DeletePropertyRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield DeletePropertyRequestV2( + propertyIri = propertyIri, + lastModificationDate = lastModificationDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } @@ -1013,22 +1083,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[CreateOntologyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: CreateOntologyRequestV2 <- CreateOntologyRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -1058,15 +1128,14 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[CanDeleteOntologyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - CanDeleteOntologyRequestV2( - ontologyIri = ontologyIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield CanDeleteOntologyRequestV2( + ontologyIri = ontologyIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessageFuture, @@ -1084,48 +1153,48 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private def deleteOntology(featureFactoryConfig: FeatureFactoryConfig): Route = path(OntologiesBasePath / Segment) { ontologyIriStr => delete { requestContext => - { - val ontologyIri = ontologyIriStr.toSmartIri - stringFormatter.checkExternalOntologyName(ontologyIri) + val ontologyIri = ontologyIriStr.toSmartIri + stringFormatter.checkExternalOntologyName(ontologyIri) - if (!ontologyIri.isKnoraOntologyIri || ontologyIri.isKnoraBuiltInDefinitionIri || !ontologyIri.getOntologySchema - .contains(ApiV2Complex)) { - throw BadRequestException(s"Invalid ontology IRI for request: $ontologyIri") - } + if ( + !ontologyIri.isKnoraOntologyIri || ontologyIri.isKnoraBuiltInDefinitionIri || !ontologyIri.getOntologySchema + .contains(ApiV2Complex) + ) { + throw BadRequestException(s"Invalid ontology IRI for request: $ontologyIri") + } - val lastModificationDateStr = requestContext.request.uri - .query() - .toMap - .getOrElse(LAST_MODIFICATION_DATE, throw BadRequestException(s"Missing parameter: $LAST_MODIFICATION_DATE")) - val lastModificationDate = stringFormatter.xsdDateTimeStampToInstant( - lastModificationDateStr, - throw BadRequestException(s"Invalid timestamp: $lastModificationDateStr")) + val lastModificationDateStr = requestContext.request.uri + .query() + .toMap + .getOrElse(LAST_MODIFICATION_DATE, throw BadRequestException(s"Missing parameter: $LAST_MODIFICATION_DATE")) + val lastModificationDate = stringFormatter.xsdDateTimeStampToInstant( + lastModificationDateStr, + throw BadRequestException(s"Invalid timestamp: $lastModificationDateStr") + ) - val requestMessageFuture: Future[DeleteOntologyRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - DeleteOntologyRequestV2( - ontologyIri = ontologyIri, - lastModificationDate = lastModificationDate, - apiRequestID = UUID.randomUUID, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val requestMessageFuture: Future[DeleteOntologyRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield DeleteOntologyRequestV2( + ontologyIri = ontologyIri, + lastModificationDate = lastModificationDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/StoreManager.scala b/webapi/src/main/scala/org/knora/webapi/store/StoreManager.scala index 8f0725d380..20d094cac2 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/StoreManager.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/StoreManager.scala @@ -35,69 +35,72 @@ import org.knora.webapi.store.triplestore.TriplestoreManager import scala.concurrent.ExecutionContext /** - * This actor receives messages for different stores, and forwards them to the corresponding store manager. - * At the moment only triple stores and Sipi are implemented, but in the future, support for different - * remote repositories will probably be needed. This place would then be the crossroad for these different kinds - * of 'stores' and their requests. - * - * @param appActor a reference to the main application actor. - */ + * This actor receives messages for different stores, and forwards them to the corresponding store manager. + * At the moment only triple stores and Sipi are implemented, but in the future, support for different + * remote repositories will probably be needed. This place would then be the crossroad for these different kinds + * of 'stores' and their requests. + * + * @param appActor a reference to the main application actor. + */ class StoreManager(appActor: ActorRef, cs: CacheService) extends Actor with ActorLogging { this: ActorMaker => /** - * The Knora Akka actor system. - */ + * The Knora Akka actor system. + */ protected implicit val system: ActorSystem = context.system /** - * The Akka actor system's execution context for futures. - */ + * The Akka actor system's execution context for futures. + */ protected implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) /** - * The Knora settings. - */ + * The Knora settings. + */ protected val settings: KnoraSettingsImpl = KnoraSettings(system) /** - * The default feature factory configuration. - */ + * The default feature factory configuration. + */ protected val defaultFeatureFactoryConfig: FeatureFactoryConfig = new KnoraSettingsFeatureFactoryConfig(settings) /** - * Starts the Triplestore Manager Actor - */ + * Starts the Triplestore Manager Actor + */ protected lazy val triplestoreManager: ActorRef = makeActor( Props( new TriplestoreManager( appActor = appActor, settings = settings, defaultFeatureFactoryConfig = defaultFeatureFactoryConfig - ) with LiveActorMaker).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), + ) with LiveActorMaker + ).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), TriplestoreManagerActorName ) /** - * Starts the IIIF Manager Actor - */ + * Starts the IIIF Manager Actor + */ protected lazy val iiifManager: ActorRef = makeActor( Props(new IIIFManager with LiveActorMaker).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - IIIFManagerActorName) + IIIFManagerActorName + ) /** - * Instantiates the Redis Manager - */ + * Instantiates the Redis Manager + */ protected lazy val cacheServiceManager: ActorRef = makeActor( Props(new CacheServiceManager(cs)).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - RedisManagerActorName) + RedisManagerActorName + ) def receive: Receive = LoggingReceive { case tripleStoreMessage: TriplestoreRequest => triplestoreManager forward tripleStoreMessage case iiifMessages: IIIFRequest => iiifManager forward iiifMessages case cacheServiceMessages: CacheServiceRequest => cacheServiceManager forward cacheServiceMessages case other => - sender ! Status.Failure(UnexpectedMessageException(s"StoreManager received an unexpected message: $other")) + sender() ! Status.Failure(UnexpectedMessageException(s"StoreManager received an unexpected message: $other")) } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/iiif/IIIFManager.scala b/webapi/src/main/scala/org/knora/webapi/store/iiif/IIIFManager.scala index 8eaec5f3bc..ff8d164529 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/iiif/IIIFManager.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/iiif/IIIFManager.scala @@ -28,27 +28,29 @@ import org.knora.webapi.messages.store.sipimessages.IIIFRequest import org.knora.webapi.settings.{KnoraDispatchers, _} /** - * Makes requests to IIIF servers. - */ + * Makes requests to IIIF servers. + */ class IIIFManager extends Actor with ActorLogging { this: ActorMaker => /** - * Constructs the [[SipiConnector]] actor (pool). - */ + * Constructs the [[SipiConnector]] actor (pool). + */ protected final def makeDefaultSipiConnector: ActorRef = - makeActor(FromConfig.props(Props[SipiConnector]).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - SipiConnectorActorName) + makeActor( + FromConfig.props(Props[SipiConnector]()).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), + SipiConnectorActorName + ) /** - * Subclasses can override this member to substitute a custom actor instead of the default SipiConnector. - */ + * Subclasses can override this member to substitute a custom actor instead of the default SipiConnector. + */ protected lazy val sipiConnector: ActorRef = makeDefaultSipiConnector def receive = LoggingReceive { case sipiMessages: IIIFRequest => sipiConnector forward sipiMessages case other => - sender ! Status.Failure(UnexpectedMessageException(s"SipiManager received an unexpected message: $other")) + sender() ! Status.Failure(UnexpectedMessageException(s"SipiManager received an unexpected message: $other")) } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/TriplestoreManager.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/TriplestoreManager.scala index e4f515d5c8..80a7cafb05 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/TriplestoreManager.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/TriplestoreManager.scala @@ -36,17 +36,18 @@ import org.knora.webapi.util.ActorUtil._ import scala.concurrent.ExecutionContext /** - * This actor receives messages representing SPARQL requests, and forwards them to instances of one of the configured - * triple stores. - * - * @param appActor a reference to the main application actor. - * @param settings the application settings. - * @param defaultFeatureFactoryConfig the application's default feature factory configuration. - */ -class TriplestoreManager(appActor: ActorRef, - settings: KnoraSettingsImpl, - defaultFeatureFactoryConfig: FeatureFactoryConfig) - extends Actor + * This actor receives messages representing SPARQL requests, and forwards them to instances of one of the configured + * triple stores. + * + * @param appActor a reference to the main application actor. + * @param settings the application settings. + * @param defaultFeatureFactoryConfig the application's default feature factory configuration. + */ +class TriplestoreManager( + appActor: ActorRef, + settings: KnoraSettingsImpl, + defaultFeatureFactoryConfig: FeatureFactoryConfig +) extends Actor with ActorLogging { this: ActorMaker => @@ -80,15 +81,16 @@ class TriplestoreManager(appActor: ActorRef, featureFactoryConfig = defaultFeatureFactoryConfig ) - override def preStart() { + override def preStart(): Unit = { log.debug("TriplestoreManagerActor: start with preStart") storeActorRef = settings.triplestoreType match { case TriplestoreTypes.HttpGraphDBSE | TriplestoreTypes.HttpGraphDBFree | TriplestoreTypes.HttpFuseki => makeActor( - FromConfig.props(Props[HttpTriplestoreConnector]).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = HttpTriplestoreActorName) - case TriplestoreTypes.EmbeddedJenaTdb => makeActor(Props[JenaTDBActor], name = EmbeddedJenaActorName) + FromConfig.props(Props[HttpTriplestoreConnector]()).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), + name = HttpTriplestoreActorName + ) + case TriplestoreTypes.EmbeddedJenaTdb => makeActor(Props[JenaTDBActor](), name = EmbeddedJenaActorName) case unknownType => throw UnsupportedTriplestoreException(s"Embedded triplestore type $unknownType not supported") } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/embedded/JenaTDBActor.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/embedded/JenaTDBActor.scala index e909b55c62..8c31822874 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/embedded/JenaTDBActor.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/embedded/JenaTDBActor.scala @@ -52,18 +52,18 @@ import scala.jdk.CollectionConverters._ import scala.concurrent.{ExecutionContextExecutor, Future} /** - * Performs SPARQL queries and updates using an embedded Jena TDB triplestore. - */ + * Performs SPARQL queries and updates using an embedded Jena TDB triplestore. + */ class JenaTDBActor extends Actor with ActorLogging { - private val system = context.system + private val system = context.system private implicit val executionContext: ExecutionContextExecutor = system.dispatcher - private val settings = KnoraSettings(system) + private val settings = KnoraSettings(system) - private val persist = settings.tripleStoreConfig.getBoolean("embedded-jena-tdb.persisted") + private val persist = settings.tripleStoreConfig.getBoolean("embedded-jena-tdb.persisted") private val loadExistingData = settings.tripleStoreConfig.getBoolean("embedded-jena-tdb.loadExistingData") - private val storagePath = Paths.get(settings.tripleStoreConfig.getString("embedded-jena-tdb.storage-path")) - private val tdbStoragePath = Paths.get(storagePath + "/db") + private val storagePath = Paths.get(settings.tripleStoreConfig.getString("embedded-jena-tdb.storage-path")) + private val tdbStoragePath = Paths.get(storagePath.toString + "/db") private val tsType = settings.triplestoreType @@ -82,9 +82,9 @@ class JenaTDBActor extends Actor with ActorLogging { var initialized: Boolean = false /** - * The actor waits with processing messages until preStart is finished, so that the loading of data can finish - * before any requests are processed. - */ + * The actor waits with processing messages until preStart is finished, so that the loading of data can finish + * before any requests are processed. + */ override def preStart(): Unit = { if (persist) { if (reloadDataOnStart || !loadExistingData) { @@ -118,11 +118,11 @@ class JenaTDBActor extends Actor with ActorLogging { } /** - * Receives a message requesting a SPARQL query or update, and returns an appropriate response message or - * [[Status.Failure]]. If a serious error occurs (i.e. an error that isn't the client's fault), this - * method first returns `Failure` to the sender, then throws an exception. - */ - def receive = { + * Receives a message requesting a SPARQL query or update, and returns an appropriate response message or + * [[Status.Failure]]. If a serious error occurs (i.e. an error that isn't the client's fault), this + * method first returns `Failure` to the sender, then throws an exception. + */ + def receive: Receive = { case SparqlSelectRequest(sparqlSelectString) => future2Message(sender(), executeSparqlSelectQuery(sparqlSelectString), log) case SparqlUpdateRequest(sparqlUpdateString) => @@ -132,18 +132,19 @@ class JenaTDBActor extends Actor with ActorLogging { case DropAllTRepositoryContent() => future2Message(sender(), Future(dropAllTriplestoreContent()), log) case InsertRepositoryContent(rdfDataObjects) => future2Message(sender(), Future(insertDataIntoTriplestore(rdfDataObjects)), log) - case HelloTriplestore(msg) if msg == tsType => sender ! HelloTriplestore(tsType) + case HelloTriplestore(msg) if msg == tsType => sender() ! HelloTriplestore(tsType) case other => - sender ! Status.Failure( - UnexpectedMessageException(s"Unexpected message $other of type ${other.getClass.getCanonicalName}")) + sender() ! Status.Failure( + UnexpectedMessageException(s"Unexpected message $other of type ${other.getClass.getCanonicalName}") + ) } /** - * Submits a SPARQL query to the embedded Jena TDB store and returns the response as a [[SparqlSelectResult]]. - * - * @param queryString the SPARQL request to be submitted. - * @return [[SparqlSelectResult]]. - */ + * Submits a SPARQL query to the embedded Jena TDB store and returns the response as a [[SparqlSelectResult]]. + * + * @param queryString the SPARQL request to be submitted. + * @return [[SparqlSelectResult]]. + */ private def executeSparqlSelectQuery(queryString: String): Future[SparqlSelectResult] = { // Start read transaction @@ -154,9 +155,9 @@ class JenaTDBActor extends Actor with ActorLogging { //println("# Content of dataset at beginning of query") //SSE.write(this.dataset) - val query: Query = QueryFactory.create(queryString) + val query: Query = QueryFactory.create(queryString) val qExec: QueryExecution = QueryExecutionFactory.create(query, this.dataset) - val resultSet: ResultSet = qExec.execSelect() + val resultSet: ResultSet = qExec.execSelect() /* Attention: the ResultSet can be only used once, i.e. it is not there anymore after use! @@ -170,27 +171,31 @@ class JenaTDBActor extends Actor with ActorLogging { // Convert the results to a list of VariableResultsRows. val variableResultsRows = resultSet.asScala.foldLeft(Vector.empty[VariableResultsRow]) { (rowAcc, row) => - val mapToWrap = resultVars.asScala.foldLeft(Map.empty[String, String]) { - case (varAcc, varName) => - Option(row.get(varName)) match { - case Some(literal: Literal) if literal.getLexicalForm.isEmpty => - varAcc // Omit variables with empty values. + val mapToWrap = resultVars.asScala.foldLeft(Map.empty[String, String]) { case (varAcc, varName) => + Option(row.get(varName)) match { + case Some(literal: Literal) if literal.getLexicalForm.isEmpty => + varAcc // Omit variables with empty values. - case Some(literal: Literal) => - varAcc + (varName -> literal.getLexicalForm) + case Some(literal: Literal) => + varAcc + (varName -> literal.getLexicalForm) - case Some(otherValue) => - varAcc + (varName -> otherValue.toString) + case Some(otherValue) => + varAcc + (varName -> otherValue.toString) - case None => varAcc - } + case None => varAcc + } } // Omit empty rows. if (mapToWrap.nonEmpty) { - rowAcc :+ VariableResultsRow(new ErrorHandlingMap(mapToWrap, { key: String => - s"No value found for SPARQL query variable '$key' in query result row" - })) + rowAcc :+ VariableResultsRow( + new ErrorHandlingMap( + mapToWrap, + { key: String => + s"No value found for SPARQL query variable '$key' in query result row" + } + ) + ) } else { rowAcc } @@ -224,12 +229,12 @@ class JenaTDBActor extends Actor with ActorLogging { } /** - * Submits a SPARQL update request to the embedded Jena TDB store, and returns a [[SparqlUpdateResponse]] if the - * operation completed successfully. - * - * @param updateString the SPARQL update to be submitted. - * @return a [[SparqlUpdateResponse]]. - */ + * Submits a SPARQL update request to the embedded Jena TDB store, and returns a [[SparqlUpdateResponse]] if the + * operation completed successfully. + * + * @param updateString the SPARQL update to be submitted. + * @return a [[SparqlUpdateResponse]]. + */ private def executeSparqlUpdateQuery(updateString: String): Future[SparqlUpdateResponse] = { // println("=============================") // println(updateString) @@ -266,13 +271,15 @@ class JenaTDBActor extends Actor with ActorLogging { } /** - * Reloads the contents of the triplestore from RDF data files. - * - * @param rdfDataObjects a list of [[RdfDataObject]] instances describing the files to be loaded. - * @return an [[ResetRepositoryContentACK]] indicating that the operation completed successfully. - */ - private def resetTripleStoreContent(rdfDataObjects: Seq[RdfDataObject], - prependDefaults: Boolean = true): Future[ResetRepositoryContentACK] = { + * Reloads the contents of the triplestore from RDF data files. + * + * @param rdfDataObjects a list of [[RdfDataObject]] instances describing the files to be loaded. + * @return an [[ResetRepositoryContentACK]] indicating that the operation completed successfully. + */ + private def resetTripleStoreContent( + rdfDataObjects: Seq[RdfDataObject], + prependDefaults: Boolean = true + ): Future[ResetRepositoryContentACK] = { val resetTriplestoreResult = for { @@ -293,10 +300,10 @@ class JenaTDBActor extends Actor with ActorLogging { } /** - * Drops all content from the triplestore. - * - * @return a [[DropAllRepositoryContentACK]] - */ + * Drops all content from the triplestore. + * + * @return a [[DropAllRepositoryContentACK]] + */ private def dropAllTriplestoreContent(): DropAllRepositoryContentACK = { // log.debug("ResetTripleStoreContent ...") @@ -334,11 +341,11 @@ class JenaTDBActor extends Actor with ActorLogging { } /** - * Inserts the data referenced in each [[RdfDataObject]] - * - * @param rdfDataObjects a sequence holding [[RdfDataObject]] - * @return a [[InsertTriplestoreContentACK]] - */ + * Inserts the data referenced in each [[RdfDataObject]] + * + * @param rdfDataObjects a sequence holding [[RdfDataObject]] + * @return a [[InsertTriplestoreContentACK]] + */ private def insertDataIntoTriplestore(rdfDataObjects: Seq[RdfDataObject]): InsertTriplestoreContentACK = { // log.debug("ResetTripleStoreContent ...") @@ -382,17 +389,17 @@ class JenaTDBActor extends Actor with ActorLogging { } /** - * Used to manually refresh the Lucene index after changing data in the triplestore. - * - * @return a [[Boolean]] denoting if the update was successful - */ + * Used to manually refresh the Lucene index after changing data in the triplestore. + * + * @return a [[Boolean]] denoting if the update was successful + */ private def updateIndex(): Boolean = { this.dataset.begin(ReadWrite.WRITE) try { // get index. val dgt: DatasetGraphText = dataset.asDatasetGraph().asInstanceOf[DatasetGraphText] - val textIndex = dgt.getTextIndex + val textIndex = dgt.getTextIndex // get entity definitions from index val entityDefinition = textIndex.getDocDef @@ -405,7 +412,7 @@ class JenaTDBActor extends Actor with ActorLogging { for (prop: Node <- entityDefinition.getPredicates(field).asScala) { val quadIter: Iterator[Quad] = dgt.find(Node.ANY, Node.ANY, prop, Node.ANY).asScala while (quadIter.hasNext) { - val quad: Quad = quadIter.next() + val quad: Quad = quadIter.next() val entity: Entity = TextQueryFuncs.entityFromQuad(entityDefinition, quad) if (entity != null) { textIndex.addEntity(entity) @@ -427,17 +434,17 @@ class JenaTDBActor extends Actor with ActorLogging { } /** - * Creates the dataset with a Lucene index attached. The triplestore dataset is either disk-backed or in-memory, - * depending on the settings. The Lucene index is always in-memory. - * - * @return a [[Dataset]] - */ + * Creates the dataset with a Lucene index attached. The triplestore dataset is either disk-backed or in-memory, + * depending on the settings. The Lucene index is always in-memory. + * + * @return a [[Dataset]] + */ private def getDataset: Dataset = { // Define which fields should be indexed by lucene val knoraBase = "http://www.knora.org/ontology/knora-base#" - val rdfs = "http://www.w3.org/2000/01/rdf-schema#" - val entDef = new EntityDefinition("uri", "text", ResourceFactory.createProperty(knoraBase, "valueHasString")) + val rdfs = "http://www.w3.org/2000/01/rdf-schema#" + val entDef = new EntityDefinition("uri", "text", ResourceFactory.createProperty(knoraBase, "valueHasString")) entDef.setPrimaryPredicate(ResourceFactory.createProperty(rdfs, "label")) entDef.setPrimaryPredicate(ResourceFactory.createProperty(knoraBase, "valueHasComment")) diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/http/HttpTriplestoreConnector.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/http/HttpTriplestoreConnector.scala index 58ce435788..de581846d0 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/http/HttpTriplestoreConnector.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/http/HttpTriplestoreConnector.scala @@ -61,22 +61,22 @@ import scala.concurrent.ExecutionContext import scala.util.{Failure, Success, Try} /** - * Submits SPARQL queries and updates to a triplestore over HTTP. Supports different triplestores, which can be configured in - * `application.conf`. - */ + * Submits SPARQL queries and updates to a triplestore over HTTP. Supports different triplestores, which can be configured in + * `application.conf`. + */ class HttpTriplestoreConnector extends Actor with ActorLogging with InstrumentationSupport { // MIME type constants. - private val mimeTypeApplicationJson = "application/json" + private val mimeTypeApplicationJson = "application/json" private val mimeTypeApplicationSparqlResultsJson = "application/sparql-results+json" - private val mimeTypeTextTurtle = "text/turtle" - private val mimeTypeApplicationSparqlUpdate = "application/sparql-update" - private val mimeTypeApplicationNQuads = "application/n-quads" + private val mimeTypeTextTurtle = "text/turtle" + private val mimeTypeApplicationSparqlUpdate = "application/sparql-update" + private val mimeTypeApplicationNQuads = "application/n-quads" - private implicit val system: ActorSystem = context.system - private val settings = KnoraSettings(system) + private implicit val system: ActorSystem = context.system + private val settings = KnoraSettings(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraBlockingDispatcher) - override val log: LoggingAdapter = akka.event.Logging(system, this.getClass.getName) + override val log: LoggingAdapter = akka.event.Logging(system, this.getClass.getName) private val triplestoreType = settings.triplestoreType @@ -85,7 +85,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat private val credsProvider: BasicCredentialsProvider = new BasicCredentialsProvider credsProvider.setCredentials( new AuthScope(targetHost.getHostName, targetHost.getPort), - new UsernamePasswordCredentials(settings.triplestoreUsername, settings.triplestorePassword)) + new UsernamePasswordCredentials(settings.triplestoreUsername, settings.triplestorePassword) + ) // Reading data should be quick, except when it is not ;-) private val queryTimeoutMillis = settings.triplestoreQueryTimeout.toMillis.toInt @@ -191,28 +192,34 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Receives a message requesting a SPARQL select or update, and returns an appropriate response message or - * [[Status.Failure]]. If a serious error occurs (i.e. an error that isn't the client's fault), this - * method first returns `Failure` to the sender, then throws an exception. - */ + * Receives a message requesting a SPARQL select or update, and returns an appropriate response message or + * [[Status.Failure]]. If a serious error occurs (i.e. an error that isn't the client's fault), this + * method first returns `Failure` to the sender, then throws an exception. + */ def receive: PartialFunction[Any, Unit] = { case SparqlSelectRequest(sparql: String) => try2Message(sender(), sparqlHttpSelect(sparql), log) case sparqlConstructRequest: SparqlConstructRequest => try2Message(sender(), sparqlHttpConstruct(sparqlConstructRequest), log) case sparqlExtendedConstructRequest: SparqlExtendedConstructRequest => try2Message(sender(), sparqlHttpExtendedConstruct(sparqlExtendedConstructRequest), log) - case SparqlConstructFileRequest(sparql: String, - graphIri: IRI, - outputFile: Path, - outputFormat: QuadFormat, - featureFactoryConfig: FeatureFactoryConfig) => - try2Message(sender(), - sparqlHttpConstructFile(sparql, graphIri, outputFile, outputFormat, featureFactoryConfig), - log) - case NamedGraphFileRequest(graphIri: IRI, - outputFile: Path, - outputFormat: QuadFormat, - featureFactoryConfig: FeatureFactoryConfig) => + case SparqlConstructFileRequest( + sparql: String, + graphIri: IRI, + outputFile: Path, + outputFormat: QuadFormat, + featureFactoryConfig: FeatureFactoryConfig + ) => + try2Message( + sender(), + sparqlHttpConstructFile(sparql, graphIri, outputFile, outputFormat, featureFactoryConfig), + log + ) + case NamedGraphFileRequest( + graphIri: IRI, + outputFile: Path, + outputFormat: QuadFormat, + featureFactoryConfig: FeatureFactoryConfig + ) => try2Message(sender(), sparqlHttpGraphFile(graphIri, outputFile, outputFormat, featureFactoryConfig), log) case NamedGraphDataRequest(graphIri: IRI) => try2Message(sender(), sparqlHttpGraphData(graphIri), log) case SparqlUpdateRequest(sparql: String) => try2Message(sender(), sparqlHttpUpdate(sparql), log) @@ -222,7 +229,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat case DropAllTRepositoryContent() => try2Message(sender(), dropAllTriplestoreContent(), log) case InsertRepositoryContent(rdfDataObjects: Seq[RdfDataObject]) => try2Message(sender(), insertDataIntoTriplestore(rdfDataObjects), log) - case HelloTriplestore(msg: String) if msg == triplestoreType => sender ! HelloTriplestore(triplestoreType) + case HelloTriplestore(msg: String) if msg == triplestoreType => sender() ! HelloTriplestore(triplestoreType) case CheckTriplestoreRequest() => try2Message(sender(), checkTriplestore(), log) case SearchIndexUpdateRequest(subjectIri: Option[String]) => try2Message(sender(), updateLuceneIndex(subjectIri), log) @@ -233,13 +240,14 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat try2Message(sender(), insertDataGraphRequest(graphContent, graphName), log) case SimulateTimeoutRequest() => try2Message(sender(), doSimulateTimeout(), log) case other => - sender ! Status.Failure( - UnexpectedMessageException(s"Unexpected message $other of type ${other.getClass.getCanonicalName}")) + sender() ! Status.Failure( + UnexpectedMessageException(s"Unexpected message $other of type ${other.getClass.getCanonicalName}") + ) } /** - * Simulates a read timeout. - */ + * Simulates a read timeout. + */ private def doSimulateTimeout(): Try[SparqlSelectResult] = { val sparql = """SELECT ?foo WHERE { | BIND("foo" AS ?foo) @@ -249,12 +257,12 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Given a SPARQL SELECT query string, runs the query, returning the result as a [[SparqlSelectResult]]. - * - * @param sparql the SPARQL SELECT query string. - * @param simulateTimeout if `true`, simulate a read timeout. - * @return a [[SparqlSelectResult]]. - */ + * Given a SPARQL SELECT query string, runs the query, returning the result as a [[SparqlSelectResult]]. + * + * @param sparql the SPARQL SELECT query string. + * @param simulateTimeout if `true`, simulate a read timeout. + * @return a [[SparqlSelectResult]]. + */ private def sparqlHttpSelect(sparql: String, simulateTimeout: Boolean = false): Try[SparqlSelectResult] = { def parseJsonResponse(sparql: String, resultStr: String): Try[SparqlSelectResult] = { val parseTry = Try { @@ -266,7 +274,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat case Failure(e) => log.error( e, - s"Couldn't parse response from triplestore:$logDelimiter$resultStr${logDelimiter}in response to SPARQL query:$logDelimiter$sparql") + s"Couldn't parse response from triplestore:$logDelimiter$resultStr${logDelimiter}in response to SPARQL query:$logDelimiter$sparql" + ) Failure(TriplestoreResponseException("Couldn't parse JSON from triplestore", e, log)) } } @@ -274,18 +283,18 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat for { // Are we using the fake triplestore? resultStr <- if (settings.useFakeTriplestore) { - // Yes: get the response from it. - Try(FakeTriplestore.data(sparql)) - } else { - // No: get the response from the real triplestore over HTTP. - getSparqlHttpResponse(sparql, isUpdate = false, simulateTimeout = simulateTimeout) - } + // Yes: get the response from it. + Try(FakeTriplestore.data(sparql)) + } else { + // No: get the response from the real triplestore over HTTP. + getSparqlHttpResponse(sparql, isUpdate = false, simulateTimeout = simulateTimeout) + } // Are we preparing a fake triplestore? _ = if (settings.prepareFakeTriplestore) { - // Yes: add the query and the response to it. - FakeTriplestore.add(sparql, resultStr, log) - } + // Yes: add the query and the response to it. + FakeTriplestore.add(sparql, resultStr, log) + } // _ = println(s"SPARQL: $logDelimiter$sparql") // _ = println(s"Result: $logDelimiter$resultStr") @@ -296,27 +305,29 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Given a SPARQL CONSTRUCT query string, runs the query, returning the result as a [[SparqlConstructResponse]]. - * - * @param sparqlConstructRequest the request message. - * @return a [[SparqlConstructResponse]] - */ + * Given a SPARQL CONSTRUCT query string, runs the query, returning the result as a [[SparqlConstructResponse]]. + * + * @param sparqlConstructRequest the request message. + * @return a [[SparqlConstructResponse]] + */ private def sparqlHttpConstruct(sparqlConstructRequest: SparqlConstructRequest): Try[SparqlConstructResponse] = { // println(logDelimiter + sparql) val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(sparqlConstructRequest.featureFactoryConfig) - def parseTurtleResponse(sparql: String, - turtleStr: String, - rdfFormatUtil: RdfFormatUtil): Try[SparqlConstructResponse] = { + def parseTurtleResponse( + sparql: String, + turtleStr: String, + rdfFormatUtil: RdfFormatUtil + ): Try[SparqlConstructResponse] = { val parseTry = Try { - val rdfModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtleStr, rdfFormat = Turtle) + val rdfModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtleStr, rdfFormat = Turtle) val statementMap: mutable.Map[IRI, Seq[(IRI, String)]] = mutable.Map.empty for (st: Statement <- rdfModel) { - val subjectIri = st.subj.stringValue + val subjectIri = st.subj.stringValue val predicateIri = st.pred.stringValue - val objectIri = st.obj.stringValue + val objectIri = st.obj.stringValue val currentStatementsForSubject: Seq[(IRI, String)] = statementMap.getOrElse(subjectIri, Vector.empty[(IRI, String)]) statementMap += (subjectIri -> (currentStatementsForSubject :+ (predicateIri, objectIri))) @@ -330,74 +341,79 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat case Failure(e) => log.error( e, - s"Couldn't parse response from triplestore:$logDelimiter$turtleStr${logDelimiter}in response to SPARQL query:$logDelimiter$sparql") + s"Couldn't parse response from triplestore:$logDelimiter$turtleStr${logDelimiter}in response to SPARQL query:$logDelimiter$sparql" + ) Failure(TriplestoreResponseException("Couldn't parse Turtle from triplestore", e, log)) } } for { - turtleStr <- getSparqlHttpResponse(sparqlConstructRequest.sparql, - isUpdate = false, - acceptMimeType = mimeTypeTextTurtle) + turtleStr <- + getSparqlHttpResponse(sparqlConstructRequest.sparql, isUpdate = false, acceptMimeType = mimeTypeTextTurtle) response <- parseTurtleResponse( - sparql = sparqlConstructRequest.sparql, - turtleStr = turtleStr, - rdfFormatUtil = rdfFormatUtil - ) + sparql = sparqlConstructRequest.sparql, + turtleStr = turtleStr, + rdfFormatUtil = rdfFormatUtil + ) } yield response } /** - * Given a SPARQL CONSTRUCT query string, runs the query, saving the result in a file. - * - * @param sparql the SPARQL CONSTRUCT query string. - * @param graphIri the named graph IRI to be used in the output file. - * @param outputFile the output file. - * @param outputFormat the output file format. - * @return a [[FileWrittenResponse]]. - */ - private def sparqlHttpConstructFile(sparql: String, - graphIri: IRI, - outputFile: Path, - outputFormat: QuadFormat, - featureFactoryConfig: FeatureFactoryConfig): Try[FileWrittenResponse] = { + * Given a SPARQL CONSTRUCT query string, runs the query, saving the result in a file. + * + * @param sparql the SPARQL CONSTRUCT query string. + * @param graphIri the named graph IRI to be used in the output file. + * @param outputFile the output file. + * @param outputFormat the output file format. + * @return a [[FileWrittenResponse]]. + */ + private def sparqlHttpConstructFile( + sparql: String, + graphIri: IRI, + outputFile: Path, + outputFormat: QuadFormat, + featureFactoryConfig: FeatureFactoryConfig + ): Try[FileWrittenResponse] = { val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) for { turtleStr <- getSparqlHttpResponse(sparql, isUpdate = false, acceptMimeType = mimeTypeTextTurtle) _ = rdfFormatUtil.turtleToQuadsFile( - rdfSource = RdfStringSource(turtleStr), - graphIri = graphIri, - outputFile = outputFile, - outputFormat = outputFormat - ) + rdfSource = RdfStringSource(turtleStr), + graphIri = graphIri, + outputFile = outputFile, + outputFormat = outputFormat + ) } yield FileWrittenResponse() } /** - * Given a SPARQL CONSTRUCT query string, runs the query, returns the result as a [[SparqlExtendedConstructResponse]]. - * - * @param sparqlExtendedConstructRequest the request message. - * @return a [[SparqlExtendedConstructResponse]] - */ + * Given a SPARQL CONSTRUCT query string, runs the query, returns the result as a [[SparqlExtendedConstructResponse]]. + * + * @param sparqlExtendedConstructRequest the request message. + * @return a [[SparqlExtendedConstructResponse]] + */ private def sparqlHttpExtendedConstruct( - sparqlExtendedConstructRequest: SparqlExtendedConstructRequest): Try[SparqlExtendedConstructResponse] = { + sparqlExtendedConstructRequest: SparqlExtendedConstructRequest + ): Try[SparqlExtendedConstructResponse] = { // println(sparql) val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(sparqlExtendedConstructRequest.featureFactoryConfig) val parseTry = for { - turtleStr <- getSparqlHttpResponse(sparqlExtendedConstructRequest.sparql, - isUpdate = false, - acceptMimeType = mimeTypeTextTurtle) + turtleStr <- getSparqlHttpResponse( + sparqlExtendedConstructRequest.sparql, + isUpdate = false, + acceptMimeType = mimeTypeTextTurtle + ) response <- SparqlExtendedConstructResponse.parseTurtleResponse( - turtleStr = turtleStr, - rdfFormatUtil = rdfFormatUtil, - log = log - ) + turtleStr = turtleStr, + rdfFormatUtil = rdfFormatUtil, + log = log + ) } yield response parseTry match { @@ -408,21 +424,21 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Updates the Lucene full-text search index. - */ - private def updateLuceneIndex(subjectIri: Option[IRI] = None): Try[SparqlUpdateResponse] = { + * Updates the Lucene full-text search index. + */ + private def updateLuceneIndex(subjectIri: Option[IRI] = None): Try[SparqlUpdateResponse] = if (triplestoreType == TriplestoreTypes.HttpGraphDBSE | triplestoreType == TriplestoreTypes.HttpGraphDBFree) { val indexUpdateSparqlString = subjectIri match { case Some(definedSubjectIri) => // A subject's content has changed. Update the index for that subject. s"""PREFIX luc: - |INSERT DATA { luc:fullTextSearchIndex luc:addToIndex <$definedSubjectIri> . } + |INSERT DATA { luc:fullTextSearchIndex luc:addToIndex <$definedSubjectIri> . } """.stripMargin case None => // Add new subjects to the index. """PREFIX luc: - |INSERT DATA { luc:fullTextSearchIndex luc:updateIndex _:b1 . } + |INSERT DATA { luc:fullTextSearchIndex luc:updateIndex _:b1 . } """.stripMargin } @@ -432,17 +448,15 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } else { Success(SparqlUpdateResponse()) } - } /** - * Performs a SPARQL update operation. - * - * @param sparqlUpdate the SPARQL update. - * @return a [[SparqlUpdateResponse]]. - */ - private def sparqlHttpUpdate(sparqlUpdate: String): Try[SparqlUpdateResponse] = { + * Performs a SPARQL update operation. + * + * @param sparqlUpdate the SPARQL update. + * @return a [[SparqlUpdateResponse]]. + */ + private def sparqlHttpUpdate(sparqlUpdate: String): Try[SparqlUpdateResponse] = // println(logDelimiter + sparqlUpdate) - for { // Send the request to the triplestore. _ <- getSparqlHttpResponse(sparqlUpdate, isUpdate = true) @@ -450,25 +464,25 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat // If we're using GraphDB, update the full-text search index. _ = updateLuceneIndex() } yield SparqlUpdateResponse() - } /** - * Performs a SPARQL ASK query. - * - * @param sparql the SPARQL ASK query. - * @return a [[SparqlAskResponse]]. - */ - def sparqlHttpAsk(sparql: String): Try[SparqlAskResponse] = { + * Performs a SPARQL ASK query. + * + * @param sparql the SPARQL ASK query. + * @return a [[SparqlAskResponse]]. + */ + def sparqlHttpAsk(sparql: String): Try[SparqlAskResponse] = for { resultString <- getSparqlHttpResponse(sparql, isUpdate = false) - _ = log.debug("sparqlHttpAsk - resultString: {}", resultString) + _ = log.debug("sparqlHttpAsk - resultString: {}", resultString) result: Boolean = resultString.parseJson.asJsObject.getFields("boolean").head.convertTo[Boolean] } yield SparqlAskResponse(result) - } - private def resetTripleStoreContent(rdfDataObjects: Seq[RdfDataObject], - prependDefaults: Boolean = true): Try[ResetRepositoryContentACK] = { + private def resetTripleStoreContent( + rdfDataObjects: Seq[RdfDataObject], + prependDefaults: Boolean = true + ): Try[ResetRepositoryContentACK] = { log.debug("resetTripleStoreContent") val resetTriplestoreResult = for { @@ -496,24 +510,26 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat val response: Try[DropAllRepositoryContentACK] = for { result: String <- getSparqlHttpResponse(dropAllSparqlString, isUpdate = true) - _ = log.debug(s"==>> Drop All Data End, Result: $result") + _ = log.debug(s"==>> Drop All Data End, Result: $result") } yield DropAllRepositoryContentACK() - response.recover { - case t: Exception => throw TriplestoreResponseException("Reset: Failed to execute DROP ALL", t, log) + response.recover { case t: Exception => + throw TriplestoreResponseException("Reset: Failed to execute DROP ALL", t, log) } } /** - * Inserts the data referenced inside the `rdfDataObjects` by appending it to a default set of `rdfDataObjects` - * based on the list defined in `application.conf` under the `app.triplestore.default-rdf-data` key. - * - * @param rdfDataObjects a sequence of paths and graph names referencing data that needs to be inserted. - * @param prependDefaults denotes if the rdfDataObjects list should be prepended with a default set. Default is `true`. - * @return [[InsertTriplestoreContentACK]] - */ - private def insertDataIntoTriplestore(rdfDataObjects: Seq[RdfDataObject], - prependDefaults: Boolean = true): Try[InsertTriplestoreContentACK] = { + * Inserts the data referenced inside the `rdfDataObjects` by appending it to a default set of `rdfDataObjects` + * based on the list defined in `application.conf` under the `app.triplestore.default-rdf-data` key. + * + * @param rdfDataObjects a sequence of paths and graph names referencing data that needs to be inserted. + * @param prependDefaults denotes if the rdfDataObjects list should be prepended with a default set. Default is `true`. + * @return [[InsertTriplestoreContentACK]] + */ + private def insertDataIntoTriplestore( + rdfDataObjects: Seq[RdfDataObject], + prependDefaults: Boolean = true + ): Try[InsertTriplestoreContentACK] = { val httpContext: HttpClientContext = makeHttpContext try { @@ -556,7 +572,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat val fileEntity = new FileEntity(inputFile.toFile, ContentType.create(mimeTypeTextTurtle, "UTF-8")) httpPost.setEntity(fileEntity) val makeResponse: CloseableHttpResponse => InsertGraphDataContentResponse = returnInsertGraphDataResponse( - graphName) + graphName + ) // Do the post request for the graph. doHttpRequest( @@ -585,9 +602,9 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Checks connection to the triplestore. - */ - private def checkTriplestore(): Try[CheckTriplestoreResponse] = { + * Checks connection to the triplestore. + */ + private def checkTriplestore(): Try[CheckTriplestoreResponse] = if (triplestoreType == TriplestoreTypes.HttpGraphDBSE | triplestoreType == TriplestoreTypes.HttpGraphDBFree) { checkGraphDBTriplestore() } else if (triplestoreType == TriplestoreTypes.HttpFuseki) { @@ -595,11 +612,11 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } else { throw UnsupportedTriplestoreException(s"Unsupported triplestore type: $triplestoreType") } - } /** - * Checks the connection to a Fuseki triplestore. - */ + * Checks the Fuseki triplestore if it is available and configured correctly. If the it is not + * configured, tries to automatically configure (initialize) the required dataset. + */ private def checkFusekiTriplestore(afterAutoInit: Boolean = false): Try[CheckTriplestoreResponse] = { import org.knora.webapi.messages.store.triplestoremessages.FusekiJsonProtocol._ @@ -625,7 +642,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat responseTry.get } - val nameShouldBe = settings.triplestoreDatabaseName + val nameShouldBe = settings.triplestoreDatabaseName val fusekiServer: FusekiServer = JsonParser(responseStr).convertTo[FusekiServer] val neededDataset: Option[FusekiDataset] = fusekiServer.datasets.find(dataset => dataset.dsName == s"/$nameShouldBe" && dataset.dsState) @@ -633,8 +650,11 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat if (neededDataset.nonEmpty) { // everything looks good Success( - CheckTriplestoreResponse(triplestoreStatus = TriplestoreStatus.ServiceAvailable, - msg = "Triplestore is available.")) + CheckTriplestoreResponse( + triplestoreStatus = TriplestoreStatus.ServiceAvailable, + msg = "Triplestore is available." + ) + ) } else { // none of the available datasets meet our requirements log.info(s"None of the active datasets meet our requirement of name: $nameShouldBe") @@ -647,7 +667,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat triplestoreStatus = TriplestoreStatus.NotInitialized, msg = s"Sorry, we tried to auto-initialize and still none of the active datasets meet our requirement of name: $nameShouldBe" - )) + ) + ) } else { // try to auto-init log.info("Triplestore auto-init is set. Trying to auto-initialize.") @@ -655,42 +676,50 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } } else { Success( - CheckTriplestoreResponse(triplestoreStatus = TriplestoreStatus.NotInitialized, - msg = s"None of the active datasets meet our requirement of name: $nameShouldBe")) + CheckTriplestoreResponse( + triplestoreStatus = TriplestoreStatus.NotInitialized, + msg = s"None of the active datasets meet our requirement of name: $nameShouldBe" + ) + ) } } } catch { case e: Exception => // println("checkRepository - exception", e) Success( - CheckTriplestoreResponse(triplestoreStatus = TriplestoreStatus.ServiceUnavailable, - msg = s"Triplestore not available: ${e.getMessage}")) + CheckTriplestoreResponse( + triplestoreStatus = TriplestoreStatus.ServiceUnavailable, + msg = s"Triplestore not available: ${e.getMessage}" + ) + ) } } /** - * Initialize the Jena Fuseki triplestore. Currently only works for - * 'knora-test' and 'knora-test-unit' repository names. To be used, the - * API needs to be started with 'KNORA_WEBAPI_TRIPLESTORE_AUTOINIT' set - * to 'true' (settings.triplestoreAutoInit). Usage is only recommended for automated testing and not for - * production use. - */ + * Initialize the Jena Fuseki triplestore. Currently only works for + * 'knora-test' and 'knora-test-unit' repository names. To be used, the + * API needs to be started with 'KNORA_WEBAPI_TRIPLESTORE_AUTOINIT' set + * to 'true' (settings.triplestoreAutoInit). This is set to `true` for tests + * (`test/resources/test.conf`).Usage is only recommended for automated + * testing and not for production use. + */ private def initJenaFusekiTriplestore(): Try[CheckTriplestoreResponse] = { val configFileName = s"webapi/scripts/fuseki-repository-config.ttl.template" - val triplestoreConfig: String = try { - // take config from the classpath and write to triplestore - FileUtil.readTextResource(configFileName).replace("@REPOSITORY@", settings.triplestoreDatabaseName) - } catch { - case _: NotFoundException => - log.error(s"Cannot initialize repository. Config $configFileName not found.") - "" - } + val triplestoreConfig: String = + try { + // take config from the classpath and write to triplestore + FileUtil.readTextResource(configFileName).replace("@REPOSITORY@", settings.triplestoreDatabaseName) + } catch { + case _: NotFoundException => + log.error(s"Cannot initialize repository. Config $configFileName not found.") + "" + } val httpContext: HttpClientContext = makeHttpContext - val httpPost: HttpPost = new HttpPost("/$/datasets") - val stringEntity = new StringEntity(triplestoreConfig, ContentType.create(mimeTypeTextTurtle)) + val httpPost: HttpPost = new HttpPost("/$/datasets") + val stringEntity = new StringEntity(triplestoreConfig, ContentType.create(mimeTypeTextTurtle)) httpPost.setEntity(stringEntity) doHttpRequest( @@ -705,8 +734,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Checks the connection to a GraphDB triplestore. - */ + * Checks the connection to a GraphDB triplestore. + */ private def checkGraphDBTriplestore(): Try[CheckTriplestoreResponse] = { // needs to be a local import or other things don't work (spray json black magic) import org.knora.webapi.messages.store.triplestoremessages.GraphDBJsonProtocol._ @@ -739,18 +768,22 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat val repositories: Seq[GraphDBRepository] = jsonArr.elements.map(_.convertTo[GraphDBRepository]) - val idShouldBe: String = settings.triplestoreDatabaseName - val sesameTypeForSEShouldBe = "owlim:MonitorRepository" + val idShouldBe: String = settings.triplestoreDatabaseName + val sesameTypeForSEShouldBe = "owlim:MonitorRepository" val sesameTypeForFreeShouldBe = "graphdb:FreeSailRepository" val neededRepo: Option[GraphDBRepository] = repositories.find(repo => - repo.id == idShouldBe && (repo.sesameType == sesameTypeForSEShouldBe || repo.sesameType == sesameTypeForFreeShouldBe)) + repo.id == idShouldBe && (repo.sesameType == sesameTypeForSEShouldBe || repo.sesameType == sesameTypeForFreeShouldBe) + ) if (neededRepo.nonEmpty) { // everything looks good Success( - CheckTriplestoreResponse(triplestoreStatus = TriplestoreStatus.ServiceAvailable, - msg = "Triplestore is available.")) + CheckTriplestoreResponse( + triplestoreStatus = TriplestoreStatus.ServiceAvailable, + msg = "Triplestore is available." + ) + ) } else { // none of the available repositories meet our requirements Success( @@ -758,23 +791,27 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat triplestoreStatus = TriplestoreStatus.NotInitialized, msg = s"None of the available repositories meet our requirements of id: $idShouldBe, sesameType: $sesameTypeForSEShouldBe or $sesameTypeForFreeShouldBe." - )) + ) + ) } } catch { case e: Exception => // println("checkRepository - exception", e) Success( - CheckTriplestoreResponse(triplestoreStatus = TriplestoreStatus.ServiceUnavailable, - msg = s"Triplestore not available: ${e.getMessage}")) + CheckTriplestoreResponse( + triplestoreStatus = TriplestoreStatus.ServiceUnavailable, + msg = s"Triplestore not available: ${e.getMessage}" + ) + ) } } /** - * Makes a triplestore URI for downloading a named graph. - * - * @param graphIri the IRI of the named graph. - * @return a triplestore-specific URI for downloading the named graph. - */ + * Makes a triplestore URI for downloading a named graph. + * + * @param graphIri the IRI of the named graph. + * @return a triplestore-specific URI for downloading the named graph. + */ private def makeNamedGraphDownloadUri(graphIri: IRI): URI = { val uriBuilder: URIBuilder = new URIBuilder(graphPath) @@ -790,20 +827,22 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Requests the contents of a named graph, saving the response in a file. - * - * @param graphIri the IRI of the named graph. - * @param outputFile the file to be written. - * @param outputFormat the output file format. - * @param featureFactoryConfig the feature factory configuration. - * @return a string containing the contents of the graph in N-Quads format. - */ - private def sparqlHttpGraphFile(graphIri: IRI, - outputFile: Path, - outputFormat: QuadFormat, - featureFactoryConfig: FeatureFactoryConfig): Try[FileWrittenResponse] = { + * Requests the contents of a named graph, saving the response in a file. + * + * @param graphIri the IRI of the named graph. + * @param outputFile the file to be written. + * @param outputFormat the output file format. + * @param featureFactoryConfig the feature factory configuration. + * @return a string containing the contents of the graph in N-Quads format. + */ + private def sparqlHttpGraphFile( + graphIri: IRI, + outputFile: Path, + outputFormat: QuadFormat, + featureFactoryConfig: FeatureFactoryConfig + ): Try[FileWrittenResponse] = { val httpContext: HttpClientContext = makeHttpContext - val httpGet = new HttpGet(makeNamedGraphDownloadUri(graphIri)) + val httpGet = new HttpGet(makeNamedGraphDownloadUri(graphIri)) httpGet.addHeader("Accept", mimeTypeTextTurtle) val makeResponse: CloseableHttpResponse => FileWrittenResponse = writeResponseFile( @@ -821,14 +860,14 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Requests the contents of a named graph, returning the response as Turtle. - * - * @param graphIri the IRI of the named graph. - * @return a string containing the contents of the graph in Turtle format. - */ + * Requests the contents of a named graph, returning the response as Turtle. + * + * @param graphIri the IRI of the named graph. + * @return a string containing the contents of the graph in Turtle format. + */ private def sparqlHttpGraphData(graphIri: IRI): Try[NamedGraphDataResponse] = { val httpContext: HttpClientContext = makeHttpContext - val httpGet = new HttpGet(makeNamedGraphDownloadUri(graphIri)) + val httpGet = new HttpGet(makeNamedGraphDownloadUri(graphIri)) httpGet.addHeader("Accept", mimeTypeTextTurtle) val makeResponse: CloseableHttpResponse => NamedGraphDataResponse = returnGraphDataAsTurtle(graphIri) @@ -841,24 +880,26 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Submits a SPARQL request to the triplestore and returns the response as a string. - * - * @param sparql the SPARQL request to be submitted. - * @param isUpdate `true` if this is an update request. - * @param acceptMimeType the MIME type to be provided in the HTTP Accept header. - * @param simulateTimeout if `true`, simulate a read timeout. - * @return the triplestore's response. - */ - private def getSparqlHttpResponse(sparql: String, - isUpdate: Boolean, - acceptMimeType: String = mimeTypeApplicationSparqlResultsJson, - simulateTimeout: Boolean = false): Try[String] = { + * Submits a SPARQL request to the triplestore and returns the response as a string. + * + * @param sparql the SPARQL request to be submitted. + * @param isUpdate `true` if this is an update request. + * @param acceptMimeType the MIME type to be provided in the HTTP Accept header. + * @param simulateTimeout if `true`, simulate a read timeout. + * @return the triplestore's response. + */ + private def getSparqlHttpResponse( + sparql: String, + isUpdate: Boolean, + acceptMimeType: String = mimeTypeApplicationSparqlResultsJson, + simulateTimeout: Boolean = false + ): Try[String] = { val httpContext: HttpClientContext = makeHttpContext val (httpClient: CloseableHttpClient, httpPost: HttpPost) = if (isUpdate) { // Send updates as application/sparql-update (as per SPARQL 1.1 Protocol §3.2.2, "UPDATE using POST directly"). - val requestEntity = new StringEntity(sparql, ContentType.create(mimeTypeApplicationSparqlUpdate, "UTF-8")) + val requestEntity = new StringEntity(sparql, ContentType.create(mimeTypeApplicationSparqlUpdate, "UTF-8")) val updateHttpPost = new HttpPost(sparqlUpdatePath) updateHttpPost.setEntity(requestEntity) (updateHttpClient, updateHttpPost) @@ -874,7 +915,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat formParams.add(new BasicNameValuePair("query", sparql)) val requestEntity: UrlEncodedFormEntity = new UrlEncodedFormEntity(formParams, Consts.UTF_8) - val queryHttpPost: HttpPost = new HttpPost(queryPath) + val queryHttpPost: HttpPost = new HttpPost(queryPath) queryHttpPost.setEntity(requestEntity) queryHttpPost.addHeader("Accept", acceptMimeType) (queryHttpClient, queryHttpPost) @@ -890,14 +931,16 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Dumps the whole repository in N-Quads format, saving the response in a file. - * - * @param outputFile the output file. - * @param featureFactoryConfig the feature factory configuration. - * @return a string containing the contents of the graph in N-Quads format. - */ - private def downloadRepository(outputFile: Path, - featureFactoryConfig: FeatureFactoryConfig): Try[FileWrittenResponse] = { + * Dumps the whole repository in N-Quads format, saving the response in a file. + * + * @param outputFile the output file. + * @param featureFactoryConfig the feature factory configuration. + * @return a string containing the contents of the graph in N-Quads format. + */ + private def downloadRepository( + outputFile: Path, + featureFactoryConfig: FeatureFactoryConfig + ): Try[FileWrittenResponse] = { val httpContext: HttpClientContext = makeHttpContext val uriBuilder: URIBuilder = new URIBuilder(repositoryDownloadPath) @@ -941,14 +984,14 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Uploads repository content from an N-Quads file. - * - * @param inputFile an N-Quads file containing the content to be uploaded to the repository. - */ + * Uploads repository content from an N-Quads file. + * + * @param inputFile an N-Quads file containing the content to be uploaded to the repository. + */ private def uploadRepository(inputFile: Path): Try[RepositoryUploadedResponse] = { val httpContext: HttpClientContext = makeHttpContext - val httpPost: HttpPost = new HttpPost(repositoryUploadPath) - val fileEntity = new FileEntity(inputFile.toFile, ContentType.create(mimeTypeApplicationNQuads, "UTF-8")) + val httpPost: HttpPost = new HttpPost(repositoryUploadPath) + val fileEntity = new FileEntity(inputFile.toFile, ContentType.create(mimeTypeApplicationNQuads, "UTF-8")) httpPost.setEntity(fileEntity) doHttpRequest( @@ -960,17 +1003,17 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Puts a data graph into the repository. - * - * @param graphContent a data graph in Turtle format to be inserted into the repository. - * @param graphName the name of the graph. - */ + * Puts a data graph into the repository. + * + * @param graphContent a data graph in Turtle format to be inserted into the repository. + * @param graphName the name of the graph. + */ private def insertDataGraphRequest(graphContent: String, graphName: String): Try[InsertGraphDataContentResponse] = { val httpContext: HttpClientContext = makeHttpContext - val uriBuilder: URIBuilder = new URIBuilder(dataInsertPath) + val uriBuilder: URIBuilder = new URIBuilder(dataInsertPath) uriBuilder.addParameter("graph", graphName) val httpPut: HttpPut = new HttpPut(uriBuilder.build()) - val requestEntity = new StringEntity(graphContent, ContentType.create(mimeTypeTextTurtle, "UTF-8")) + val requestEntity = new StringEntity(graphContent, ContentType.create(mimeTypeTextTurtle, "UTF-8")) httpPut.setEntity(requestEntity) val makeResponse: CloseableHttpResponse => InsertGraphDataContentResponse = returnInsertGraphDataResponse(graphName) @@ -983,12 +1026,12 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Formulate HTTP context. - * - * @return httpContext with credentials and authorization - */ + * Formulate HTTP context. + * + * @return httpContext with credentials and authorization + */ private def makeHttpContext: HttpClientContext = { - val authCache: AuthCache = new BasicAuthCache + val authCache: AuthCache = new BasicAuthCache val basicAuth: BasicScheme = new BasicScheme authCache.put(targetHost, basicAuth) @@ -999,22 +1042,24 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } /** - * Makes an HTTP connection to the triplestore, and delegates processing of the response - * to a function. - * - * @param client the HTTP client to be used for the request. - * @param request the request to be sent. - * @param context the request context to be used. - * @param processResponse a function that processes the HTTP response. - * @param simulateTimeout if `true`, simulate a read timeout. - * @tparam T the return type of `processResponse`. - * @return the return value of `processResponse`. - */ - private def doHttpRequest[T](client: CloseableHttpClient, - request: HttpRequest, - context: HttpClientContext, - processResponse: CloseableHttpResponse => T, - simulateTimeout: Boolean = false): Try[T] = { + * Makes an HTTP connection to the triplestore, and delegates processing of the response + * to a function. + * + * @param client the HTTP client to be used for the request. + * @param request the request to be sent. + * @param context the request context to be used. + * @param processResponse a function that processes the HTTP response. + * @param simulateTimeout if `true`, simulate a read timeout. + * @tparam T the return type of `processResponse`. + * @return the return value of `processResponse`. + */ + private def doHttpRequest[T]( + client: CloseableHttpClient, + request: HttpRequest, + context: HttpClientContext, + processResponse: CloseableHttpResponse => T, + simulateTimeout: Boolean = false + ): Try[T] = { // Make an Option wrapper for the response, so we can close it if we get one, // even if an error occurs. var maybeResponse: Option[CloseableHttpResponse] = None @@ -1024,7 +1069,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat throw new java.net.SocketTimeoutException("Simulated read timeout") } - val start = System.currentTimeMillis() + val start = System.currentTimeMillis() val response = client.execute(targetHost, request, context) maybeResponse = Some(response) val statusCode: Int = response.getStatusLine.getStatusCode @@ -1040,7 +1085,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat case Some(responseEntityStr) => log.error(s"Triplestore responded with HTTP code $statusCode: $responseEntityStr") throw TriplestoreResponseException( - s"Triplestore responded with HTTP code $statusCode: $responseEntityStr") + s"Triplestore responded with HTTP code $statusCode: $responseEntityStr" + ) case None => log.error(s"Triplestore responded with HTTP code $statusCode") @@ -1076,16 +1122,15 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } } - def returnResponseAsString(response: CloseableHttpResponse): String = { + def returnResponseAsString(response: CloseableHttpResponse): String = Option(response.getEntity) match { case None => "" case Some(responseEntity) => EntityUtils.toString(responseEntity, StandardCharsets.UTF_8) } - } - def returnGraphDataAsTurtle(graphIri: IRI)(response: CloseableHttpResponse): NamedGraphDataResponse = { + def returnGraphDataAsTurtle(graphIri: IRI)(response: CloseableHttpResponse): NamedGraphDataResponse = Option(response.getEntity) match { case None => log.error(s"Triplestore returned no content for graph $graphIri") @@ -1096,14 +1141,14 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat turtle = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8) ) } - } def returnUploadResponse: CloseableHttpResponse => RepositoryUploadedResponse = { _ => RepositoryUploadedResponse() } - def returnInsertGraphDataResponse(graphName: String)( - response: CloseableHttpResponse): InsertGraphDataContentResponse = { + def returnInsertGraphDataResponse( + graphName: String + )(response: CloseableHttpResponse): InsertGraphDataContentResponse = Option(response.getEntity) match { case None => log.error(s"$graphName could not be inserted into Triplestore.") @@ -1113,32 +1158,31 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat InsertGraphDataContentResponse() } - } - /** - * Represents a named graph IRI and the file format that the graph should be written in. - * - * @param graphIri the named graph IRI. - * @param quadFormat the file format. - */ + * Represents a named graph IRI and the file format that the graph should be written in. + * + * @param graphIri the named graph IRI. + * @param quadFormat the file format. + */ case class GraphIriAndFormat(graphIri: IRI, quadFormat: QuadFormat) /** - * Writes an HTTP response to a file. - * - * @param outputFile the output file. - * @param featureFactoryConfig the feature factory configuration. - * @param maybeGraphIriAndFormat a graph IRI and quad format for the output file. If defined, the response - * is parsed as Turtle and converted to the output format, with the graph IRI - * added to each statement. Otherwise, the response is written as-is to the - * output file. - * @param response the response to be read. - * @return a [[FileWrittenResponse]]. - */ - def writeResponseFile(outputFile: Path, - featureFactoryConfig: FeatureFactoryConfig, - maybeGraphIriAndFormat: Option[GraphIriAndFormat] = None)( - response: CloseableHttpResponse): FileWrittenResponse = { + * Writes an HTTP response to a file. + * + * @param outputFile the output file. + * @param featureFactoryConfig the feature factory configuration. + * @param maybeGraphIriAndFormat a graph IRI and quad format for the output file. If defined, the response + * is parsed as Turtle and converted to the output format, with the graph IRI + * added to each statement. Otherwise, the response is written as-is to the + * output file. + * @param response the response to be read. + * @return a [[FileWrittenResponse]]. + */ + def writeResponseFile( + outputFile: Path, + featureFactoryConfig: FeatureFactoryConfig, + maybeGraphIriAndFormat: Option[GraphIriAndFormat] = None + )(response: CloseableHttpResponse): FileWrittenResponse = Option(response.getEntity) match { case Some(responseEntity: HttpEntity) => // Are we converting the response to a quad format? @@ -1186,5 +1230,4 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat throw TriplestoreResponseException(s"Triplestore returned no content for for repository dump") } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/util/cache/CacheUtil.scala b/webapi/src/main/scala/org/knora/webapi/util/cache/CacheUtil.scala index 5afbd1a002..a368cc217e 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/cache/CacheUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/cache/CacheUtil.scala @@ -154,7 +154,7 @@ object CacheUtil { * @param cacheName the name of the cache. * @param key the cache key as a [[String]] */ - def remove(cacheName: String, key: String) { + def remove(cacheName: String, key: String): () = { val cacheManager = CacheManager.getInstance() val cacheOption = Option(cacheManager.getCache(cacheName)) diff --git a/webapi/src/main/twirl/BUILD.bazel b/webapi/src/main/twirl/BUILD.bazel index aa4103b9a4..a5679313be 100644 --- a/webapi/src/main/twirl/BUILD.bazel +++ b/webapi/src/main/twirl/BUILD.bazel @@ -32,3 +32,31 @@ genrule( cmd = "jar -cf \"$@\" $(locations :twirl_sources)", # tools = ["jar"], ) + +twirl_templates( + name = "twirl_new_sources", + srcs = [ + "org/knora/webapi/queries/sparql/v2/isPropertyUsed.scala.txt" + ], + additional_imports = [ + "play.twirl.api.TwirlFeatureImports._", + "play.twirl.api.TwirlHelperImports._", + "play.twirl.api.Html", + "play.twirl.api.JavaScript", + "play.twirl.api.Txt", + "play.twirl.api.Xml", + ], + source_directory = ".", +) + +scala_library( + name = "library", + srcs = [":twirl_new_sources"], + deps = [ + "//webapi/src/main/scala/org/knora/webapi", + "@maven//:com_typesafe_akka_akka_actor_2_13", + "@maven//:com_typesafe_play_twirl_api_2_13", + "@maven//:org_scala_lang_modules_scala_xml_2_13", + "@maven//:org_slf4j_slf4j_api", + ], +) diff --git a/webapi/src/main/twirl/org/knora/webapi/queries/sparql/v2/isPropertyUsed.scala.txt b/webapi/src/main/twirl/org/knora/webapi/queries/sparql/v2/isPropertyUsed.scala.txt new file mode 100644 index 0000000000..1f76eb27aa --- /dev/null +++ b/webapi/src/main/twirl/org/knora/webapi/queries/sparql/v2/isPropertyUsed.scala.txt @@ -0,0 +1,52 @@ +@* + * 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 . + *@ + +@import org.knora.webapi.IRI + +@* + * Checks whether a property is used (i.e., is the property of any statements). + * + * @param triplestore the name of the triplestore being used. + * @param propertyIri the IRI of the property. + * @param ignoreKnoraConstraints if true, knora-base:subjectClassConstraint and knora-base:objectClassConstraint will be ignored. + * This is necessary when modifying the cardinalities of a class. + * @param ignoreRdfSubjectAndObject if true, rdf:subject and rdf:object will be ignored. This is necessary when checking + * for references to a resource that is to be erased. + *@ +@(triplestore: String, + internalPropertyIri: IRI, + ignoreKnoraConstraints: Boolean = false, + ignoreRdfSubjectAndObject: Boolean = false) + +PREFIX rdf: +PREFIX rdfs: +PREFIX owl: +PREFIX xsd: +PREFIX knora-base: + +ASK +@* Ensure that inference is not used in this query. *@ +@if(triplestore.startsWith("graphdb")) { + FROM +} +WHERE { + BIND(IRI("@internalPropertyIri") AS ?property) + + ?s ?property ?o . +} diff --git a/webapi/src/test/scala/org/knora/webapi/BUILD.bazel b/webapi/src/test/scala/org/knora/webapi/BUILD.bazel index ab9bc242d7..9f21e285d4 100644 --- a/webapi/src/test/scala/org/knora/webapi/BUILD.bazel +++ b/webapi/src/test/scala/org/knora/webapi/BUILD.bazel @@ -10,11 +10,13 @@ filegroup( "E2ESpec.scala", "ITKnoraFakeSpec.scala", "ITKnoraLiveSpec.scala", + "IntegrationSpec.scala", "KnoraFakeCore.scala", "ManagersWithMockedSipi.scala", "R2RSpec.scala", - "RedisTestContainer.scala", - "TestContainers.scala", + "TestContainerFuseki.scala", + "TestContainerRedis.scala", + "TestContainersAll.scala", "TestProbeMaker.scala", "UnitSpec.scala", "//webapi/src/test/scala/org/knora/webapi/e2e:srcs", diff --git a/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala b/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala index 03fc01918d..9f623938c6 100644 --- a/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala @@ -65,7 +65,7 @@ object CoreSpec { .dropWhile(_ matches "(java.lang.Thread|.*CoreSpec.?$)") val reduced = s.lastIndexWhere(_ == clazz.getName) match { case -1 => s - case z => s drop (z + 1) + case z => s drop (z + 1) } reduced.head.replaceFirst(""".*\.""", "").replaceAll("[^a-zA-Z_0-9]", "_") } @@ -85,24 +85,31 @@ abstract class CoreSpec(_system: ActorSystem) this( ActorSystem( name, - TestContainers.PortConfig.withFallback(ConfigFactory.load(config.withFallback(CoreSpec.defaultConfig))))) + TestContainersAll.PortConfig.withFallback(ConfigFactory.load(config.withFallback(CoreSpec.defaultConfig))) + ) + ) def this(config: Config) = this( ActorSystem( CoreSpec.getCallerName(classOf[CoreSpec]), - TestContainers.PortConfig.withFallback(ConfigFactory.load(config.withFallback(CoreSpec.defaultConfig))))) + TestContainersAll.PortConfig.withFallback(ConfigFactory.load(config.withFallback(CoreSpec.defaultConfig))) + ) + ) - def this(name: String) = this(ActorSystem(name, TestContainers.PortConfig.withFallback(ConfigFactory.load()))) + def this(name: String) = this(ActorSystem(name, TestContainersAll.PortConfig.withFallback(ConfigFactory.load()))) def this() = this( - ActorSystem(CoreSpec.getCallerName(classOf[CoreSpec]), - TestContainers.PortConfig.withFallback(ConfigFactory.load()))) + ActorSystem( + CoreSpec.getCallerName(classOf[CoreSpec]), + TestContainersAll.PortConfig.withFallback(ConfigFactory.load()) + ) + ) /* needed by the core trait */ - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) // can be overridden in individual spec @@ -119,7 +126,7 @@ abstract class CoreSpec(_system: ActorSystem) // The main application actor forwards messages to the responder manager and the store manager. val responderManager: ActorRef = appActor - val storeManager: ActorRef = appActor + val storeManager: ActorRef = appActor val responderData: ResponderData = ResponderData( system = system, @@ -148,10 +155,8 @@ abstract class CoreSpec(_system: ActorSystem) // memusage() } - final override def afterAll(): () = { + final override def afterAll(): () = appActor ! AppStop() - // memusage() - } protected def loadTestData(rdfDataObjects: Seq[RdfDataObject]): Unit = { logger.info("Loading test data started ...") @@ -163,11 +168,14 @@ abstract class CoreSpec(_system: ActorSystem) logger.info("Loading load ontologies into cache started ...") Try( - Await.result(appActor ? LoadOntologiesRequestV2( - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ), - 1 minute)) match { + 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}") } @@ -178,15 +186,4 @@ abstract class CoreSpec(_system: ActorSystem) case Failure(e) => logger.error(s"Flushing Redis cache failed: ${e.getMessage}") } } - - def memusage(): Unit = { - // memory info - val mb = 1024 * 1024 - val runtime = Runtime.getRuntime - println("** Used Memory: " + (runtime.totalMemory - runtime.freeMemory) / mb) - println("** Free Memory: " + runtime.freeMemory / mb) - println("** Total Memory: " + runtime.totalMemory / mb) - println("** Max Memory: " + runtime.maxMemory / mb) - } - } diff --git a/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala index 5d89b819d8..bcc5f68af7 100644 --- a/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala @@ -54,9 +54,9 @@ object E2ESpec { } /** - * This class can be used in End-to-End testing. It starts the Knora-API server - * and provides access to settings and logging. - */ + * This class can be used in End-to-End testing. It starts the Knora-API server + * and provides access to settings and logging. + */ class E2ESpec(_system: ActorSystem) extends Core with StartupUtils @@ -71,20 +71,20 @@ class E2ESpec(_system: ActorSystem) /* constructors */ def this(name: String, config: Config) = - this(ActorSystem(name, TestContainers.PortConfig.withFallback(config.withFallback(E2ESpec.defaultConfig)))) + this(ActorSystem(name, TestContainersAll.PortConfig.withFallback(config.withFallback(E2ESpec.defaultConfig)))) def this(config: Config) = - this(ActorSystem("E2ETest", TestContainers.PortConfig.withFallback(config.withFallback(E2ESpec.defaultConfig)))) + this(ActorSystem("E2ETest", TestContainersAll.PortConfig.withFallback(config.withFallback(E2ESpec.defaultConfig)))) - def this(name: String) = this(ActorSystem(name, TestContainers.PortConfig.withFallback(E2ESpec.defaultConfig))) + def this(name: String) = this(ActorSystem(name, TestContainersAll.PortConfig.withFallback(E2ESpec.defaultConfig))) - def this() = this(ActorSystem("E2ETest", TestContainers.PortConfig.withFallback(E2ESpec.defaultConfig))) + def this() = this(ActorSystem("E2ETest", TestContainersAll.PortConfig.withFallback(E2ESpec.defaultConfig))) /* needed by the core trait */ - implicit lazy val system: ActorSystem = _system - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit lazy val system: ActorSystem = _system + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) // can be overridden in individual spec @@ -122,15 +122,16 @@ class E2ESpec(_system: ActorSystem) } - override def afterAll(): Unit = { + override def afterAll(): Unit = /* Stop the server when everything else has finished */ appActor ! AppStop() - } protected def loadTestData(rdfDataObjects: Seq[RdfDataObject]): Unit = { logger.info("Loading test data started ...") - val request = Post(baseApiUrl + "/admin/store/ResetTriplestoreContent", - HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint)) + val request = Post( + baseApiUrl + "/admin/store/ResetTriplestoreContent", + HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint) + ) val response = Http().singleRequest(request) Try(Await.result(response, 479999.milliseconds)) match { @@ -147,7 +148,7 @@ class E2ESpec(_system: ActorSystem) protected def responseToJsonLDDocument(httpResponse: HttpResponse): JsonLDDocument = { val responseBodyFuture: Future[String] = httpResponse.entity.toStrict(10.seconds).map(_.data.decodeString("UTF-8")) - val responseBodyStr = Await.result(responseBodyFuture, 10.seconds) + val responseBodyStr = Await.result(responseBodyFuture, 10.seconds) JsonLDUtil.parseJsonLD(responseBodyStr) } @@ -157,7 +158,7 @@ class E2ESpec(_system: ActorSystem) } protected def doGetRequest(urlPath: String): String = { - val request = Get(s"$baseApiUrl$urlPath") + val request = Get(s"$baseApiUrl$urlPath") val response: HttpResponse = singleAwaitingRequest(request) responseToString(response) } @@ -183,27 +184,28 @@ class E2ESpec(_system: ActorSystem) } /** - * Reads or writes a test data file. - * The written test data files can be found under: - * ./bazel-out/darwin-fastbuild/testlogs///test.outputs/outputs.zip - * - * @param responseAsString the API response received from Knora. - * @param file the file in which the expected API response is stored. - * @param writeFile if `true`, writes the response to the file and returns it, otherwise returns the current contents of the file. - * @return the expected response. - */ - protected def readOrWriteTextFile(responseAsString: String, file: Path, writeFile: Boolean = false): String = { + * Reads or writes a test data file. + * The written test data files can be found under: + * ./bazel-out/darwin-fastbuild/testlogs///test.outputs/outputs.zip + * + * @param responseAsString the API response received from Knora. + * @param file the file in which the expected API response is stored. + * @param writeFile if `true`, writes the response to the file and returns it, otherwise returns the current contents of the file. + * @return the expected response. + */ + protected def readOrWriteTextFile(responseAsString: String, file: Path, writeFile: Boolean = false): String = if (writeFile) { // Per default only read access is allowed in the bazel sandbox. // This workaround allows to save test output. val testOutputDir: Path = Paths.get(sys.env("TEST_UNDECLARED_OUTPUTS_DIR")) - val newOutputFile = testOutputDir.resolve(file) + val newOutputFile = testOutputDir.resolve(file) Files.createDirectories(newOutputFile.getParent) - FileUtil.writeTextFile(newOutputFile, - responseAsString.replaceAll(settings.externalSipiIIIFGetUrl, "IIIF_BASE_URL")) + FileUtil.writeTextFile( + newOutputFile, + responseAsString.replaceAll(settings.externalSipiIIIFGetUrl, "IIIF_BASE_URL") + ) responseAsString } else { FileUtil.readTextFile(file).replaceAll("IIIF_BASE_URL", settings.externalSipiIIIFGetUrl) } - } } diff --git a/webapi/src/test/scala/org/knora/webapi/ITKnoraFakeSpec.scala b/webapi/src/test/scala/org/knora/webapi/ITKnoraFakeSpec.scala index 7702272b1c..a0910a0c8d 100644 --- a/webapi/src/test/scala/org/knora/webapi/ITKnoraFakeSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/ITKnoraFakeSpec.scala @@ -45,9 +45,9 @@ object ITKnoraFakeSpec { } /** - * This class can be used in End-to-End testing. It starts a Fake Knora server and - * provides access to settings and logging. - */ + * This class can be used in End-to-End testing. It starts a Fake Knora server and + * provides access to settings and logging. + */ class ITKnoraFakeSpec(_system: ActorSystem) extends Core with KnoraFakeCore @@ -59,20 +59,25 @@ class ITKnoraFakeSpec(_system: ActorSystem) /* constructors */ def this(name: String, config: Config) = - this(ActorSystem(name, TestContainers.PortConfig.withFallback(config.withFallback(ITKnoraFakeSpec.defaultConfig)))) + this( + ActorSystem(name, TestContainersAll.PortConfig.withFallback(config.withFallback(ITKnoraFakeSpec.defaultConfig))) + ) def this(config: Config) = this( - ActorSystem("IntegrationTests", - TestContainers.PortConfig.withFallback(config.withFallback(ITKnoraFakeSpec.defaultConfig)))) + ActorSystem( + "IntegrationTests", + TestContainersAll.PortConfig.withFallback(config.withFallback(ITKnoraFakeSpec.defaultConfig)) + ) + ) def this(name: String) = - this(ActorSystem(name, TestContainers.PortConfig.withFallback(ITKnoraFakeSpec.defaultConfig))) + this(ActorSystem(name, TestContainersAll.PortConfig.withFallback(ITKnoraFakeSpec.defaultConfig))) def this() = - this(ActorSystem("IntegrationTests", TestContainers.PortConfig.withFallback(ITKnoraFakeSpec.defaultConfig))) + this(ActorSystem("IntegrationTests", TestContainersAll.PortConfig.withFallback(ITKnoraFakeSpec.defaultConfig))) /* needed by the core trait */ - implicit lazy val system: ActorSystem = _system - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit lazy val system: ActorSystem = _system + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) /* Needs to be initialized before any responders */ @@ -81,7 +86,7 @@ class ITKnoraFakeSpec(_system: ActorSystem) val log: LoggingAdapter = akka.event.Logging(system, this.getClass) - protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl + protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl protected val baseInternalSipiUrl: String = settings.internalSipiBaseUrl protected val baseExternalSipiUrl: String = settings.externalSipiBaseUrl @@ -103,17 +108,15 @@ class ITKnoraFakeSpec(_system: ActorSystem) } protected def getResponseString(request: HttpRequest): String = { - val response = singleAwaitingRequest(request) + val response = singleAwaitingRequest(request) val responseBodyStr = Await.result(Unmarshal(response.entity).to[String], 6.seconds) assert(response.status === StatusCodes.OK, s",\n REQUEST: $request,\n RESPONSE: $responseBodyStr") responseBodyStr } - protected def checkResponseOK(request: HttpRequest): Unit = { + protected def checkResponseOK(request: HttpRequest): Unit = getResponseString(request) - } - protected def getResponseJson(request: HttpRequest): JsObject = { + protected def getResponseJson(request: HttpRequest): JsObject = getResponseString(request).parseJson.asJsObject - } } diff --git a/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala index 955ee7ccbd..ac5cbc22f1 100644 --- a/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala @@ -53,9 +53,9 @@ object ITKnoraLiveSpec { } /** - * This class can be used in End-to-End testing. It starts the Knora server and - * provides access to settings and logging. - */ + * This class can be used in End-to-End testing. It starts the Knora server and + * provides access to settings and logging. + */ class ITKnoraLiveSpec(_system: ActorSystem) extends Core with StartupUtils @@ -69,20 +69,25 @@ class ITKnoraLiveSpec(_system: ActorSystem) /* constructors */ def this(name: String, config: Config) = - this(ActorSystem(name, TestContainers.PortConfig.withFallback(config.withFallback(ITKnoraLiveSpec.defaultConfig)))) + this( + ActorSystem(name, TestContainersAll.PortConfig.withFallback(config.withFallback(ITKnoraLiveSpec.defaultConfig))) + ) def this(config: Config) = this( - ActorSystem("IntegrationTests", - TestContainers.PortConfig.withFallback(config.withFallback(ITKnoraLiveSpec.defaultConfig)))) + ActorSystem( + "IntegrationTests", + TestContainersAll.PortConfig.withFallback(config.withFallback(ITKnoraLiveSpec.defaultConfig)) + ) + ) def this(name: String) = - this(ActorSystem(name, TestContainers.PortConfig.withFallback(ITKnoraLiveSpec.defaultConfig))) + this(ActorSystem(name, TestContainersAll.PortConfig.withFallback(ITKnoraLiveSpec.defaultConfig))) def this() = - this(ActorSystem("IntegrationTests", TestContainers.PortConfig.withFallback(ITKnoraLiveSpec.defaultConfig))) + this(ActorSystem("IntegrationTests", TestContainersAll.PortConfig.withFallback(ITKnoraLiveSpec.defaultConfig))) /* needed by the core trait (represents the KnoraTestCore trait)*/ - implicit lazy val system: ActorSystem = _system - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit lazy val system: ActorSystem = _system + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) // can be overridden in individual spec @@ -97,7 +102,7 @@ class ITKnoraLiveSpec(_system: ActorSystem) lazy val appActor: ActorRef = system.actorOf(Props(new ApplicationActor with LiveManagers), name = APPLICATION_MANAGER_ACTOR_NAME) - protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl + protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl protected val baseInternalSipiUrl: String = settings.internalSipiBaseUrl protected val baseExternalSipiUrl: String = settings.externalSipiBaseUrl @@ -119,14 +124,13 @@ class ITKnoraLiveSpec(_system: ActorSystem) loadTestData(rdfDataObjects) } - override def afterAll(): Unit = { + override def afterAll(): Unit = /* Stop the server when everything else has finished */ appActor ! AppStop() - } protected def checkIfSipiIsRunning(): Unit = { // This requires that (1) fileserver.docroot is set in Sipi's config file and (2) it contains a file test.html. - val request = Get(baseInternalSipiUrl + "/server/test.html") + val request = Get(baseInternalSipiUrl + "/server/test.html") val response = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, s"Sipi is probably not running: ${response.status}") if (response.status.isSuccess()) logger.info("Sipi is running.") @@ -135,8 +139,10 @@ class ITKnoraLiveSpec(_system: ActorSystem) protected def loadTestData(rdfDataObjects: Seq[RdfDataObject]): Unit = { logger.info("Loading test data started ...") - val request = Post(baseApiUrl + "/admin/store/ResetTriplestoreContent", - HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint)) + val request = Post( + baseApiUrl + "/admin/store/ResetTriplestoreContent", + HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint) + ) singleAwaitingRequest(request, 479999.milliseconds) logger.info("... loading test data done.") } @@ -150,17 +156,16 @@ class ITKnoraLiveSpec(_system: ActorSystem) responseBodyStr } else { throw AssertionException( - s"Got HTTP ${response.status.intValue}\n REQUEST: $request,\n RESPONSE: $responseBodyStr") + s"Got HTTP ${response.status.intValue}\n REQUEST: $request,\n RESPONSE: $responseBodyStr" + ) } } - protected def checkResponseOK(request: HttpRequest): Unit = { + protected def checkResponseOK(request: HttpRequest): Unit = getResponseStringOrThrow(request) - } - protected def getResponseJson(request: HttpRequest): JsObject = { + protected def getResponseJson(request: HttpRequest): JsObject = getResponseStringOrThrow(request).parseJson.asJsObject - } protected def singleAwaitingRequest(request: HttpRequest, duration: Duration = 15999.milliseconds): HttpResponse = { val responseFuture: Future[HttpResponse] = Http().singleRequest(request) @@ -173,57 +178,60 @@ class ITKnoraLiveSpec(_system: ActorSystem) } /** - * Represents a file to be uploaded to Sipi. - * - * @param path the path of the file. - * @param mimeType the MIME type of the file. - */ + * Represents a file to be uploaded to Sipi. + * + * @param path the path of the file. + * @param mimeType the MIME type of the file. + */ protected case class FileToUpload(path: String, mimeType: ContentType) /** - * Represents an image file to be uploaded to Sipi. - * - * @param fileToUpload the file to be uploaded. - * @param width the image's width in pixels. - * @param height the image's height in pixels. - */ + * Represents an image file to be uploaded to Sipi. + * + * @param fileToUpload the file to be uploaded. + * @param width the image's width in pixels. + * @param height the image's height in pixels. + */ protected case class InputFile(fileToUpload: FileToUpload, width: Int, height: Int) /** - * Represents the information that Sipi returns about each file that has been uploaded. - * - * @param originalFilename the original filename that was submitted to Sipi. - * @param internalFilename Sipi's internal filename for the stored temporary file. - * @param temporaryUrl the URL at which the temporary file can be accessed. - * @param fileType `image`, `text`, or `document`. - */ - protected case class SipiUploadResponseEntry(originalFilename: String, - internalFilename: String, - temporaryUrl: String, - fileType: String) + * Represents the information that Sipi returns about each file that has been uploaded. + * + * @param originalFilename the original filename that was submitted to Sipi. + * @param internalFilename Sipi's internal filename for the stored temporary file. + * @param temporaryUrl the URL at which the temporary file can be accessed. + * @param fileType `image`, `text`, or `document`. + */ + protected case class SipiUploadResponseEntry( + originalFilename: String, + internalFilename: String, + temporaryUrl: String, + fileType: String + ) /** - * Represents Sipi's response to a file upload request. - * - * @param uploadedFiles the information about each file that was uploaded. - */ + * Represents Sipi's response to a file upload request. + * + * @param uploadedFiles the information about each file that was uploaded. + */ protected case class SipiUploadResponse(uploadedFiles: Seq[SipiUploadResponseEntry]) object SipiUploadResponseJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol { implicit val sipiUploadResponseEntryFormat: RootJsonFormat[SipiUploadResponseEntry] = jsonFormat4( - SipiUploadResponseEntry) + SipiUploadResponseEntry + ) implicit val sipiUploadResponseFormat: RootJsonFormat[SipiUploadResponse] = jsonFormat1(SipiUploadResponse) } import SipiUploadResponseJsonProtocol._ /** - * Uploads a file to Sipi and returns the information in Sipi's response. - * - * @param loginToken the login token to be included in the request to Sipi. - * @param filesToUpload the files to be uploaded. - * @return a [[SipiUploadResponse]] representing Sipi's response. - */ + * Uploads a file to Sipi and returns the information in Sipi's response. + * + * @param loginToken the login token to be included in the request to Sipi. + * @param filesToUpload the files to be uploaded. + * @return a [[SipiUploadResponse]] representing Sipi's response. + */ protected def uploadToSipi(loginToken: String, filesToUpload: Seq[FileToUpload]): SipiUploadResponse = { // Make a multipart/form-data request containing the files. diff --git a/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala b/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala new file mode 100644 index 0000000000..cc4849e775 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala @@ -0,0 +1,159 @@ +/* + * 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 . + */ + +package org.knora.webapi + +import akka.actor.{ActorRef, ActorSystem} +import akka.dispatch.MessageDispatcher +import akka.util.Timeout +import com.typesafe.config.{Config, ConfigFactory} +import com.typesafe.scalalogging.LazyLogging +import org.knora.webapi.messages.StringFormatter +import org.knora.webapi.messages.store.triplestoremessages.{ + CheckTriplestoreRequest, + CheckTriplestoreResponse, + RdfDataObject, + ResetRepositoryContent, + TriplestoreStatus +} +import org.knora.webapi.settings.{KnoraDispatchers, KnoraSettings, KnoraSettingsImpl} +import org.scalatest.BeforeAndAfterAll +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.{AnyWordSpecLike, AsyncWordSpecLike} + +import scala.concurrent.{Await, ExecutionContext, Future} +import scala.language.postfixOps +import scala.util.{Failure, Success, Try} +import akka.pattern.ask +import org.knora.webapi.messages.store.triplestoremessages.TriplestoreStatus.TriplestoreStatus +import zio.Schedule.Decision +import zio.clock.Clock +import zio.console.{Console, putStrLn} +import zio.duration._ +import zio.{BootstrapRuntime, IO, Runtime, Schedule, Task, ZIO} + +object IntegrationSpec { + + /* + Loads the following (first-listed are higher priority): + - system properties (e.g., -Dconfig.resource=fuseki.conf) + - test/resources/application.conf + - main/resources/application.conf + */ + val defaultConfig: Config = ConfigFactory.load() + + /* Copied from: akka/akka-testkit/src/test/scala/akka/testkit/AkkaSpec.scala */ + def getCallerName(clazz: Class[_]): String = { + val s = (Thread.currentThread.getStackTrace map (_.getClassName) drop 1) + .dropWhile(_ matches "(java.lang.Thread|.*UnitSpec.?$)") + val reduced = s.lastIndexWhere(_ == clazz.getName) match { + case -1 => s + case z => s drop (z + 1) + } + reduced.head.replaceFirst(""".*\.""", "").replaceAll("[^a-zA-Z_0-9]", "_") + } +} + +abstract class IntegrationSpec(_config: Config) + extends AsyncWordSpecLike + with Matchers + with BeforeAndAfterAll + with LazyLogging { + + def this() = + this(UnitSpec.defaultConfig) + + implicit val system: ActorSystem = + ActorSystem( + CoreSpec.getCallerName(classOf[IntegrationSpec]), + TestContainerFuseki.PortConfig.withFallback(IntegrationSpec.defaultConfig) + ) + + implicit val settings: KnoraSettingsImpl = KnoraSettings(system) + override implicit val executionContext: ExecutionContext = + system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) + implicit val timeout: Timeout = settings.defaultTimeout + + // needs to be initialized early on + StringFormatter.initForTest() + + protected def waitForReadyTriplestore(actorRef: ActorRef): Unit = { + logger.info("Waiting for triplestore to be ready ...") + implicit val ctx: MessageDispatcher = system.dispatchers.lookup(KnoraDispatchers.KnoraBlockingDispatcher) + val checkTriplestore: ZIO[Any, Throwable, Unit] = for { + checkResult <- ZIO.fromTry( + Try( + Await + .result(actorRef ? CheckTriplestoreRequest(), 1.second.asScala) + .asInstanceOf[CheckTriplestoreResponse] + ) + ) + + value <- if (checkResult.triplestoreStatus == TriplestoreStatus.ServiceAvailable) { + ZIO.effectTotal(logger.info("... triplestore is ready.")) + } else { + ZIO.fail( + new Exception( + s"Triplestore not ready: ${checkResult.triplestoreStatus}" + ) + ) + } + } yield value + + implicit val rt: Runtime[Clock with Console] = Runtime.default + rt.unsafeRun(checkTriplestore.retry(ScheduleUtil.schedule)) + } + + protected def loadTestData( + actorRef: ActorRef, + rdfDataObjects: Seq[RdfDataObject] = Seq.empty[RdfDataObject] + ): Unit = { + logger.info("Loading test data started ...") + implicit val timeout: Timeout = Timeout(settings.defaultTimeout) + Try(Await.result(actorRef ? ResetRepositoryContent(rdfDataObjects), 479999.milliseconds.asScala)) match { + case Success(res) => logger.info("... loading test data done.") + case Failure(e) => logger.error(s"Loading test data failed: ${e.getMessage}") + } + } +} + +object ScheduleUtil { + + /** + * Retry every second for 60 times, i.e., 60 seconds in total. + */ + def schedule[A]: Schedule[Console, Any, (Long, Long)] = Schedule.spaced(1.second) && Schedule + .recurs(60) + .onDecision({ + case Decision.Done(_) => putStrLn(s"done trying").orDie + case Decision.Continue(attempt, _, _) => putStrLn(s"attempt #$attempt").orDie + }) +} + +//// ZIO helpers //// +object LegacyRuntime { + + /** + * Transforms a [[Task]] into a [[Future]]. + */ + def fromTask[Res](body: => Task[Res]): Future[Res] = + MyRuntime.unsafeRunToFuture(body) +} + +object MyRuntime extends BootstrapRuntime diff --git a/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala index e9fbcd7edd..695d9c6688 100644 --- a/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala @@ -52,8 +52,8 @@ import scala.concurrent.{Await, ExecutionContext, Future} import scala.language.postfixOps /** - * R(oute)2R(esponder) Spec base class. Please, for any new E2E tests, use E2ESpec. - */ + * R(oute)2R(esponder) Spec base class. Please, for any new E2E tests, use E2ESpec. + */ class R2RSpec extends Core with StartupUtils @@ -67,8 +67,10 @@ class R2RSpec /* needed by the core trait */ implicit lazy val _system: ActorSystem = ActorSystem( actorSystemNameFrom(getClass), - TestContainers.PortConfig.withFallback( - ConfigFactory.parseString(testConfigSource).withFallback(ConfigFactory.load()))) + TestContainersAll.PortConfig.withFallback( + ConfigFactory.parseString(testConfigSource).withFallback(ConfigFactory.load()) + ) + ) implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(_system) @@ -93,11 +95,12 @@ class R2RSpec lazy val appActor: ActorRef = system.actorOf( Props(new ApplicationActor with LiveManagers).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = APPLICATION_MANAGER_ACTOR_NAME) + 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 storeManager: ActorRef = appActor val routeData: KnoraRouteData = KnoraRouteData( system = system, @@ -121,14 +124,13 @@ class R2RSpec loadTestData(rdfDataObjects) } - override def afterAll(): () = { + override def afterAll(): () = /* Stop the server when everything else has finished */ appActor ! AppStop() - } protected def responseToJsonLDDocument(httpResponse: HttpResponse): JsonLDDocument = { val responseBodyFuture: Future[String] = httpResponse.entity.toStrict(5.seconds).map(_.data.decodeString("UTF-8")) - val responseBodyStr = Await.result(responseBodyFuture, 5.seconds) + val responseBodyStr = Await.result(responseBodyFuture, 5.seconds) JsonLDUtil.parseJsonLD(responseBodyStr) } @@ -146,35 +148,38 @@ class R2RSpec implicit val timeout: Timeout = Timeout(settings.defaultTimeout) Await.result(appActor ? ResetRepositoryContent(rdfDataObjects), 5 minutes) - Await.result(appActor ? LoadOntologiesRequestV2( - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ), - 30 seconds) + Await.result( + appActor ? LoadOntologiesRequestV2( + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + ), + 30 seconds + ) } /** - * Reads or writes a test data file. - * The written test data files can be found under: - * ./bazel-out/darwin-fastbuild/testlogs///test.outputs/outputs.zip - * - * @param responseAsString the API response received from Knora. - * @param file the file in which the expected API response is stored. - * @param writeFile if `true`, writes the response to the file and returns it, otherwise returns the current contents of the file. - * @return the expected response. - */ - protected def readOrWriteTextFile(responseAsString: String, file: Path, writeFile: Boolean = false): String = { + * Reads or writes a test data file. + * The written test data files can be found under: + * ./bazel-out/darwin-fastbuild/testlogs///test.outputs/outputs.zip + * + * @param responseAsString the API response received from Knora. + * @param file the file in which the expected API response is stored. + * @param writeFile if `true`, writes the response to the file and returns it, otherwise returns the current contents of the file. + * @return the expected response. + */ + protected def readOrWriteTextFile(responseAsString: String, file: Path, writeFile: Boolean = false): String = if (writeFile) { // Per default only read access is allowed in the bazel sandbox. // This workaround allows to save test output. val testOutputDir: Path = Paths.get(sys.env("TEST_UNDECLARED_OUTPUTS_DIR")) - val newOutputFile = testOutputDir.resolve(file) + val newOutputFile = testOutputDir.resolve(file) Files.createDirectories(newOutputFile.getParent) - FileUtil.writeTextFile(newOutputFile, - responseAsString.replaceAll(settings.externalSipiIIIFGetUrl, "IIIF_BASE_URL")) + FileUtil.writeTextFile( + newOutputFile, + responseAsString.replaceAll(settings.externalSipiIIIFGetUrl, "IIIF_BASE_URL") + ) responseAsString } else { FileUtil.readTextFile(file).replaceAll("IIIF_BASE_URL", settings.externalSipiIIIFGetUrl) } - } } diff --git a/webapi/src/test/scala/org/knora/webapi/TestContainerFuseki.scala b/webapi/src/test/scala/org/knora/webapi/TestContainerFuseki.scala new file mode 100644 index 0000000000..1a10ac52e2 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/TestContainerFuseki.scala @@ -0,0 +1,48 @@ +/* + * 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 . + */ + +package org.knora.webapi + +import com.typesafe.config.{Config, ConfigFactory} +import org.testcontainers.containers.GenericContainer +import org.testcontainers.utility.DockerImageName + +import scala.jdk.CollectionConverters._ + +/** + * Provides the Fuseki container necessary for running tests. + */ +object TestContainerFuseki { + + val FusekiImageName: DockerImageName = DockerImageName.parse("bazel/docker/knora-jena-fuseki:image") + val FusekiContainer = new GenericContainer(FusekiImageName) + + FusekiContainer.withExposedPorts(3030) + FusekiContainer.withEnv("ADMIN_PASSWORD", "test") + FusekiContainer.withEnv("JVM_ARGS", "-Xmx3G") + FusekiContainer.start() + + private val portMap = Map( + "app.triplestore.fuseki.port" -> FusekiContainer.getFirstMappedPort + ).asJava + + // all tests need to be configured with these ports. + val PortConfig: Config = + ConfigFactory.parseMap(portMap, "Ports from RedisTestContainer").withFallback(ConfigFactory.load()) +} diff --git a/webapi/src/test/scala/org/knora/webapi/RedisTestContainer.scala b/webapi/src/test/scala/org/knora/webapi/TestContainerRedis.scala similarity index 89% rename from webapi/src/test/scala/org/knora/webapi/RedisTestContainer.scala rename to webapi/src/test/scala/org/knora/webapi/TestContainerRedis.scala index 652203c7d7..bcef49339c 100644 --- a/webapi/src/test/scala/org/knora/webapi/RedisTestContainer.scala +++ b/webapi/src/test/scala/org/knora/webapi/TestContainerRedis.scala @@ -26,12 +26,12 @@ import org.testcontainers.utility.DockerImageName import scala.jdk.CollectionConverters._ /** - * Provides all containers necessary for running tests. - */ -object RedisTestContainer { + * Provides the Redis container necessary for running tests. + */ +object TestContainerRedis { val RedisImageName: DockerImageName = DockerImageName.parse("redis:5") - val RedisContainer = new GenericContainer(RedisImageName) + val RedisContainer = new GenericContainer(RedisImageName) RedisContainer.withExposedPorts(6379) RedisContainer.start() diff --git a/webapi/src/test/scala/org/knora/webapi/TestContainers.scala b/webapi/src/test/scala/org/knora/webapi/TestContainersAll.scala similarity index 83% rename from webapi/src/test/scala/org/knora/webapi/TestContainers.scala rename to webapi/src/test/scala/org/knora/webapi/TestContainersAll.scala index 8b1727b56f..3fbb8ec1a5 100644 --- a/webapi/src/test/scala/org/knora/webapi/TestContainers.scala +++ b/webapi/src/test/scala/org/knora/webapi/TestContainersAll.scala @@ -28,9 +28,9 @@ import org.testcontainers.utility.DockerImageName import scala.jdk.CollectionConverters._ /** - * Provides all containers necessary for running tests. - */ -object TestContainers { + * Provides all containers necessary for running tests. + */ +object TestContainersAll { // get local IP address, which we need for SIPI val localIpAddress: String = NetworkInterface.getNetworkInterfaces.asScala.toSeq @@ -40,14 +40,14 @@ object TestContainers { .getOrElse(throw new UnknownHostException("No suitable network interface found")) val FusekiImageName: DockerImageName = DockerImageName.parse("bazel/docker/knora-jena-fuseki:image") - val FusekiContainer = new GenericContainer(FusekiImageName) + val FusekiContainer = new GenericContainer(FusekiImageName) FusekiContainer.withExposedPorts(3030) FusekiContainer.withEnv("ADMIN_PASSWORD", "test") FusekiContainer.withEnv("JVM_ARGS", "-Xmx3G") FusekiContainer.start() val SipiImageName: DockerImageName = DockerImageName.parse("bazel/docker/knora-sipi:image") - val SipiContainer = new GenericContainer(SipiImageName) + val SipiContainer = new GenericContainer(SipiImageName) SipiContainer.withExposedPorts(1024) SipiContainer.withEnv("SIPI_EXTERNAL_PROTOCOL", "http") SipiContainer.withEnv("SIPI_EXTERNAL_HOSTNAME", "0.0.0.0") @@ -55,15 +55,16 @@ object TestContainers { SipiContainer.withEnv("SIPI_WEBAPI_HOSTNAME", localIpAddress) SipiContainer.withEnv("SIPI_WEBAPI_PORT", "3333") SipiContainer.withCommand("--config=/sipi/config/sipi.knora-docker-config.lua") - SipiContainer.withClasspathResourceMapping("/sipi/config/sipi.knora-docker-config.lua", - "/sipi/config/sipi.knora-docker-config.lua", - BindMode.READ_ONLY) + SipiContainer.withClasspathResourceMapping( + "/sipi/config/sipi.knora-docker-config.lua", + "/sipi/config/sipi.knora-docker-config.lua", + BindMode.READ_ONLY + ) SipiContainer.start() // Container needs to be started to get the random IP val sipiIp: String = SipiContainer.getHost - val sipiPort: Int = SipiContainer.getFirstMappedPort - + val sipiPort: Int = SipiContainer.getFirstMappedPort // The new default is the inmem cache implementation, so no need // for a container @@ -75,8 +76,8 @@ object TestContainers { private val portMap = Map( "app.triplestore.fuseki.port" -> FusekiContainer.getFirstMappedPort, - "app.sipi.internal-host" -> sipiIp, - "app.sipi.internal-port" -> sipiPort + "app.sipi.internal-host" -> sipiIp, + "app.sipi.internal-port" -> sipiPort // "app.cache-service.redis.port" -> RedisContainer.getFirstMappedPort ).asJava diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/BUILD.bazel b/webapi/src/test/scala/org/knora/webapi/e2e/v2/BUILD.bazel index b17a37911f..6f14577fb2 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/BUILD.bazel +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/BUILD.bazel @@ -74,6 +74,7 @@ scala_test( size = "medium", # 300s srcs = [ "OntologyV2R2RSpec.scala", + "OntologyModels.scala" ], data = [ "//knora-ontologies", diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyModels.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyModels.scala new file mode 100644 index 0000000000..909d6f9464 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyModels.scala @@ -0,0 +1,219 @@ +/* + * Copyright © 2015-2021 the contributors (see Contributors.md). + * + * This file is part of the DaSCH Service Platform. + * + * The DaSCH Service Platform 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. + * + * The DaSCH Service Platform 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 the DaSCH Service Platform. If not, see + * . + */ + +package org.knora.webapi.e2e.v2 + +import org.knora.webapi.sharedtestdata.SharedOntologyTestDataADM + +import java.time.Instant +import scala.annotation.tailrec + +final case class LangString(language: String, value: String) + +sealed abstract case class CreateClassRequest private (value: String) +object CreateClassRequest { + def make( + ontologyName: String, + lastModificationDate: Instant, + className: String, + label: LangString, + comment: LangString + ): CreateClassRequest = { + val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" + val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" + + val value = s"""{ + | "@id" : "$ontologyId", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$lastModificationDate" + | }, + | "@graph" : [ { + | "@id" : "$ontologyName:$className", + | "@type" : "owl:Class", + | "rdfs:label" : { + | "@language" : "${label.language}", + | "@value" : "${label.value}" + | }, + | "rdfs:comment" : { + | "@language" : "${comment.language}", + | "@value" : "${comment.value}" + | }, + | "rdfs:subClassOf" : [ + | { + | "@id": "knora-api:Resource" + | } + | ] + | } ], + | "@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#", + | "$ontologyName" : "$ontologyId#" + | } + |}""".stripMargin + new CreateClassRequest(value) {} + } +} + +sealed trait PropertyValueType { + val value: String +} +object PropertyValueType { + case object TextValue extends PropertyValueType { + val value = "knora-api:TextValue" + } + case object IntValue extends PropertyValueType { + val value = "knora-api:IntValue" + } +} + +sealed abstract case class CreatePropertyRequest private (value: String) +object CreatePropertyRequest { + def make( + ontologyName: String, + lastModificationDate: Instant, + propertyName: String, + subjectClassName: String, + propertyType: PropertyValueType, + label: LangString, + comment: LangString + ): CreatePropertyRequest = { + val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" + val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" + val value = s"""{ + | "@id" : "$ontologyId", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$lastModificationDate" + | }, + | "@graph" : [ { + | "@id" : "$ontologyName:$propertyName", + | "@type" : "owl:ObjectProperty", + | "knora-api:subjectType" : { + | "@id" : "$ontologyName:$subjectClassName" + | }, + | "knora-api:objectType" : { + | "@id" : "${propertyType.value}" + | }, + | "rdfs:comment" : { + | "@language" : "${comment.language}", + | "@value" : "${comment.value}" + | }, + | "rdfs:label" : { + | "@language" : "${label.language}", + | "@value" : "${label.value}" + | }, + | "rdfs:subPropertyOf" : { + | "@id" : "knora-api:hasValue" + | } + | } ], + | "@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#", + | "$ontologyName" : "$ontologyId#" + | } + |}""".stripMargin + new CreatePropertyRequest(value) {} + } +} + +sealed trait CardinalityRestriction { + val cardinality: String + val value: Int +} +object CardinalityRestriction { + case object MaxCardinalityOne extends CardinalityRestriction { + val cardinality = "owl:maxCardinality" + val value = 1 + } +} + +final case class Property(ontology: String, property: String) +final case class Restriction(restriction: CardinalityRestriction, onProperty: Property) { + def stringify(): String = + s""" + | { + | "@type": "owl:Restriction", + | "${restriction.cardinality}" : ${restriction.value}, + | "owl:onProperty" : { + | "@id" : "${onProperty.ontology}:${onProperty.property}" + | } + | } + |""".stripMargin +} + +sealed abstract case class AddCardinalitiesRequest private (value: String) +object AddCardinalitiesRequest { + + @tailrec + private def stringifyRestrictions(restrictions: List[Restriction], acc: String = ""): String = + restrictions match { + case Nil => acc + case r :: Nil => acc + r.stringify() + case r :: rest => stringifyRestrictions(restrictions = rest, acc + r.stringify() + ", ") + } + + def make( + ontologyName: String, + lastModificationDate: Instant, + className: String, + restrictions: List[Restriction] + ): AddCardinalitiesRequest = { + val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" + val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" + val restrictionsString: String = stringifyRestrictions(restrictions) + val value = s""" + |{ + | "@id" : "$ontologyId", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$lastModificationDate" + | }, + | "@graph" : [ { + | "@id" : "$ontologyName:$className", + | "@type" : "owl:Class", + | "rdfs:subClassOf" : [ + | $restrictionsString + | ] + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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#", + | "$ontologyName" : "$ontologyId#" + | } + |} + """.stripMargin + new AddCardinalitiesRequest(value) {} + } +} diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala index decf58c377..209e8e7071 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala @@ -1,9 +1,5 @@ package org.knora.webapi.e2e.v2 -import java.nio.file.{Files, Path, Paths} -import java.net.URLEncoder -import java.time.Instant - import akka.actor.ActorSystem import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.{Accept, BasicHttpCredentials} @@ -17,52 +13,65 @@ import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.messages.v2.responder.ontologymessages.{InputOntologyV2, TestResponseParsingModeV2} import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} -import org.knora.webapi.routing.v2.OntologiesRouteV2 +import org.knora.webapi.routing.v2.{OntologiesRouteV2, ResourcesRouteV2} import org.knora.webapi.sharedtestdata.{SharedOntologyTestDataADM, SharedTestDataADM} import org.knora.webapi.util._ import spray.json._ +import java.net.URLEncoder +import java.nio.file.{Files, Path, Paths} +import java.time.Instant import scala.concurrent.ExecutionContextExecutor object OntologyV2R2RSpec { private val anythingUserProfile = SharedTestDataADM.anythingAdminUser - private val anythingUsername = anythingUserProfile.email + private val anythingUsername = anythingUserProfile.email private val superUserProfile = SharedTestDataADM.superUser - private val superUsername = superUserProfile.email + private val superUsername = superUserProfile.email private val password = SharedTestDataADM.testPass } /** - * End-to-end test specification for API v2 ontology routes. - */ + * End-to-end test specification for API v2 ontology routes. + */ class OntologyV2R2RSpec extends R2RSpec { import OntologyV2R2RSpec._ override def testConfigSource: String = """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" + |# akka.loglevel = "DEBUG" + |# akka.stdout-loglevel = "DEBUG" """.stripMargin private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - private val ontologiesPath = DSPApiDirectives.handleErrors(system) { new OntologiesRouteV2(routeData).knoraApiPath } + private val ontologiesPath = DSPApiDirectives.handleErrors(system)(new OntologiesRouteV2(routeData).knoraApiPath) + private val resourcesPath = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).knoraApiPath) implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) implicit val ec: ExecutionContextExecutor = system.dispatcher + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // If true, the existing expected response files are overwritten with the HTTP GET responses from the server. // If false, the responses from the server are compared to the contents fo the expected response files. + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! private val writeTestDataFiles = false override lazy val rdfDataObjects = List( - RdfDataObject(path = "test_data/ontologies/example-box.ttl", - name = "http://www.knora.org/ontology/shared/example-box"), - RdfDataObject(path = "test_data/ontologies/minimal-onto.ttl", name = "http://www.knora.org/ontology/0001/minimal") + RdfDataObject( + path = "test_data/ontologies/example-box.ttl", + name = "http://www.knora.org/ontology/shared/example-box" + ), + RdfDataObject(path = "test_data/ontologies/minimal-onto.ttl", name = "http://www.knora.org/ontology/0001/minimal"), + RdfDataObject( + path = "test_data/ontologies/freetest-onto.ttl", + name = "http://www.knora.org/ontology/0001/freetest" + ), + RdfDataObject(path = "test_data/all_data/freetest-data.ttl", name = "http://www.knora.org/data/0001/freetest") ) // Directory path for generated client test data @@ -72,46 +81,47 @@ class OntologyV2R2RSpec extends R2RSpec { private val clientTestDataCollector = new ClientTestDataCollector(settings) /** - * Represents an HTTP GET test that requests ontology information. - * - * @param urlPath the URL path to be used in the request. - * @param fileBasename the basename of the test data file containing the expected response. - * @param maybeClientTestDataBasename the basename of the client test data file, if any, to be collected by - * [[org.knora.webapi.e2e.ClientTestDataCollector]]. - * @param disableWrite if true, this [[HttpGetTest]] will not write the expected response file when `writeFile` is called. - * This is useful if two tests share the same file. - */ - private case class HttpGetTest(urlPath: String, - fileBasename: String, - maybeClientTestDataBasename: Option[String] = None, - disableWrite: Boolean = false) { + * Represents an HTTP GET test that requests ontology information. + * + * @param urlPath the URL path to be used in the request. + * @param fileBasename the basename of the test data file containing the expected response. + * @param maybeClientTestDataBasename the basename of the client test data file, if any, to be collected by + * [[org.knora.webapi.e2e.ClientTestDataCollector]]. + * @param disableWrite if true, this [[HttpGetTest]] will not write the expected response file when `writeFile` is called. + * This is useful if two tests share the same file. + */ + private case class HttpGetTest( + urlPath: String, + fileBasename: String, + maybeClientTestDataBasename: Option[String] = None, + disableWrite: Boolean = false + ) { def makeFile(mediaType: MediaType.NonBinary): Path = { val fileSuffix = mediaType.fileExtensions.head Paths.get(s"test_data/ontologyR2RV2/$fileBasename.$fileSuffix") } /** - * Writes the expected response file. - * - * @param responseStr the contents of the file to be written. - * @param mediaType the media type of the response. - */ - def writeFile(responseStr: String, mediaType: MediaType.NonBinary): Unit = { + * Writes the expected response file. + * + * @param responseStr the contents of the file to be written. + * @param mediaType the media type of the response. + */ + def writeFile(responseStr: String, mediaType: MediaType.NonBinary): Unit = if (!disableWrite) { // Per default only read access is allowed in the bazel sandbox. // This workaround allows to save test output. val testOutputDir = Paths.get(sys.env("TEST_UNDECLARED_OUTPUTS_DIR")) - val file = makeFile(mediaType) + val file = makeFile(mediaType) val newOutputFile = testOutputDir.resolve(file) Files.createDirectories(newOutputFile.getParent) FileUtil.writeTextFile(newOutputFile, responseStr) } - } /** - * If `maybeClientTestDataBasename` is defined, stores the response string in [[org.knora.webapi.e2e.ClientTestDataCollector]]. - */ - def storeClientTestData(responseStr: String): Unit = { + * If `maybeClientTestDataBasename` is defined, stores the response string in [[org.knora.webapi.e2e.ClientTestDataCollector]]. + */ + def storeClientTestData(responseStr: String): Unit = maybeClientTestDataBasename match { case Some(clientTestDataBasename) => clientTestDataCollector.addFile( @@ -127,23 +137,21 @@ class OntologyV2R2RSpec extends R2RSpec { case None => () } - } /** - * Reads the expected response file. - * - * @param mediaType the media type of the response. - * @return the contents of the file. - */ - def readFile(mediaType: MediaType.NonBinary): String = { + * Reads the expected response file. + * + * @param mediaType the media type of the response. + * @return the contents of the file. + */ + def readFile(mediaType: MediaType.NonBinary): String = FileUtil.readTextFile(makeFile(mediaType)) - } } // URL-encoded IRIs for use as URL segments in HTTP GET tests. - private val anythingProjectSegment = URLEncoder.encode(SharedTestDataADM.ANYTHING_PROJECT_IRI, "UTF-8") + private val anythingProjectSegment = URLEncoder.encode(SharedTestDataADM.ANYTHING_PROJECT_IRI, "UTF-8") private val incunabulaProjectSegment = URLEncoder.encode(SharedTestDataADM.INCUNABULA_PROJECT_IRI, "UTF-8") - private val beolProjectSegment = URLEncoder.encode(SharedTestDataADM.BEOL_PROJECT_IRI, "UTF-8") + private val beolProjectSegment = URLEncoder.encode(SharedTestDataADM.BEOL_PROJECT_IRI, "UTF-8") private val knoraApiSimpleOntologySegment = URLEncoder.encode(OntologyConstants.KnoraApiV2Simple.KnoraApiOntologyIri, "UTF-8") private val knoraApiWithValueObjectsOntologySegment = @@ -190,9 +198,11 @@ class OntologyV2R2RSpec extends R2RSpec { // The URLs and expected response files for each HTTP GET test. private val httpGetTests = Seq( - HttpGetTest(urlPath = "/v2/ontologies/metadata", - fileBasename = "allOntologyMetadata", - maybeClientTestDataBasename = Some("all-ontology-metadata-response")), + HttpGetTest( + urlPath = "/v2/ontologies/metadata", + fileBasename = "allOntologyMetadata", + maybeClientTestDataBasename = Some("all-ontology-metadata-response") + ), HttpGetTest( urlPath = s"/v2/ontologies/metadata/$anythingProjectSegment", fileBasename = "anythingOntologyMetadata", @@ -208,23 +218,31 @@ class OntologyV2R2RSpec extends R2RSpec { fileBasename = "beolOntologyMetadata", maybeClientTestDataBasename = Some("get-ontologies-project-beol-response") ), - HttpGetTest(urlPath = s"/v2/ontologies/allentities/$knoraApiSimpleOntologySegment", - fileBasename = "knoraApiOntologySimple"), - HttpGetTest(urlPath = "/ontology/knora-api/simple/v2", - fileBasename = "knoraApiOntologySimple", - disableWrite = true), + HttpGetTest( + urlPath = s"/v2/ontologies/allentities/$knoraApiSimpleOntologySegment", + fileBasename = "knoraApiOntologySimple" + ), + HttpGetTest( + urlPath = "/ontology/knora-api/simple/v2", + fileBasename = "knoraApiOntologySimple", + disableWrite = true + ), HttpGetTest( urlPath = s"/v2/ontologies/allentities/$knoraApiWithValueObjectsOntologySegment", fileBasename = "knoraApiOntologyWithValueObjects", maybeClientTestDataBasename = Some("knora-api-ontology") ), - HttpGetTest(urlPath = "/ontology/knora-api/v2", - fileBasename = "knoraApiOntologyWithValueObjects", - disableWrite = true), + HttpGetTest( + urlPath = "/ontology/knora-api/v2", + fileBasename = "knoraApiOntologyWithValueObjects", + disableWrite = true + ), HttpGetTest(urlPath = "/ontology/salsah-gui/v2", fileBasename = "salsahGuiOntology"), HttpGetTest(urlPath = "/ontology/standoff/v2", fileBasename = "standoffOntologyWithValueObjects"), - HttpGetTest(urlPath = s"/v2/ontologies/allentities/$incunabulaOntologySimpleSegment", - fileBasename = "incunabulaOntologySimple"), + HttpGetTest( + urlPath = s"/v2/ontologies/allentities/$incunabulaOntologySimpleSegment", + fileBasename = "incunabulaOntologySimple" + ), HttpGetTest( urlPath = s"/v2/ontologies/allentities/$incunabulaOntologyWithValueObjectsSegment", fileBasename = "incunabulaOntologyWithValueObjects", @@ -232,20 +250,30 @@ class OntologyV2R2RSpec extends R2RSpec { ), HttpGetTest(urlPath = s"/v2/ontologies/classes/$knoraApiDateSegment", fileBasename = "knoraApiDate"), HttpGetTest(urlPath = s"/v2/ontologies/classes/$knoraApiDateValueSegment", fileBasename = "knoraApiDateValue"), - HttpGetTest(urlPath = s"/v2/ontologies/properties/$knoraApiSimpleHasColorSegment", - fileBasename = "knoraApiSimpleHasColor"), - HttpGetTest(urlPath = s"/v2/ontologies/properties/$knoraApiWithValueObjectsHasColorSegment", - fileBasename = "knoraApiWithValueObjectsHasColor"), - HttpGetTest(urlPath = s"/v2/ontologies/properties/$incunabulaSimplePubdateSegment", - fileBasename = "incunabulaSimplePubDate"), - HttpGetTest(urlPath = s"/v2/ontologies/properties/$incunabulaWithValueObjectsPubDateSegment", - fileBasename = "incunabulaWithValueObjectsPubDate"), + HttpGetTest( + urlPath = s"/v2/ontologies/properties/$knoraApiSimpleHasColorSegment", + fileBasename = "knoraApiSimpleHasColor" + ), + HttpGetTest( + urlPath = s"/v2/ontologies/properties/$knoraApiWithValueObjectsHasColorSegment", + fileBasename = "knoraApiWithValueObjectsHasColor" + ), + HttpGetTest( + urlPath = s"/v2/ontologies/properties/$incunabulaSimplePubdateSegment", + fileBasename = "incunabulaSimplePubDate" + ), + HttpGetTest( + urlPath = s"/v2/ontologies/properties/$incunabulaWithValueObjectsPubDateSegment", + fileBasename = "incunabulaWithValueObjectsPubDate" + ), HttpGetTest( urlPath = s"/v2/ontologies/classes/$incunabulaWithValueObjectsPageSegment/$incunabulaWithValueObjectsBookSegment", fileBasename = "incunabulaPageAndBookWithValueObjects" ), - HttpGetTest(urlPath = s"/v2/ontologies/allentities/$boxOntologyWithValueObjectsSegment", - fileBasename = "boxOntologyWithValueObjects"), + HttpGetTest( + urlPath = s"/v2/ontologies/allentities/$boxOntologyWithValueObjectsSegment", + fileBasename = "boxOntologyWithValueObjects" + ), HttpGetTest( urlPath = s"/v2/ontologies/allentities/$minimalOntologyWithValueObjects", fileBasename = "minimalOntologyWithValueObjects", @@ -261,9 +289,11 @@ class OntologyV2R2RSpec extends R2RSpec { fileBasename = "anythingThingWithAllLanguages", maybeClientTestDataBasename = Some("get-class-anything-thing-with-allLanguages-response") ), - HttpGetTest(urlPath = s"/v2/ontologies/classes/$imagesBild", - fileBasename = "imagesBild", - maybeClientTestDataBasename = Some("get-class-image-bild-response")), + HttpGetTest( + urlPath = s"/v2/ontologies/classes/$imagesBild", + fileBasename = "imagesBild", + maybeClientTestDataBasename = Some("get-class-image-bild-response") + ), HttpGetTest( urlPath = s"/v2/ontologies/classes/$incunabulaBook", fileBasename = "incunabulaBook", @@ -284,9 +314,11 @@ class OntologyV2R2RSpec extends R2RSpec { fileBasename = "anythingHasDate", maybeClientTestDataBasename = Some("get-property-DateValue-response") ), - HttpGetTest(urlPath = s"/v2/ontologies/properties/$imagesTitel", - fileBasename = "imagesTitel", - maybeClientTestDataBasename = Some("get-property-textValue-response")), + HttpGetTest( + urlPath = s"/v2/ontologies/properties/$imagesTitel", + fileBasename = "imagesTitel", + maybeClientTestDataBasename = Some("get-property-textValue-response") + ), HttpGetTest( urlPath = s"/v2/ontologies/properties/$incunabulaPartOf", fileBasename = "incunabulaPartOf", @@ -301,14 +333,15 @@ class OntologyV2R2RSpec extends R2RSpec { RdfMediaTypes.`application/rdf+xml` ) - private val fooIri = new MutableTestIri - private val barIri = new MutableTestIri + private val fooIri = new MutableTestIri + private val barIri = new MutableTestIri private var fooLastModDate: Instant = Instant.now private var barLastModDate: Instant = Instant.now private var anythingLastModDate: Instant = Instant.parse("2017-12-19T15:23:42.166Z") + private var freetestLastModDate: Instant = Instant.parse("2012-12-12T12:12:12.12Z") - private val uselessIri = new MutableTestIri + private val uselessIri = new MutableTestIri private var uselessLastModDate: Instant = Instant.now private def getPropertyIrisFromResourceClassResponse(responseJsonDoc: JsonLDDocument): Set[SmartIri] = { @@ -348,7 +381,7 @@ class OntologyV2R2RSpec extends R2RSpec { val existingFile: Path = httpGetTest.makeFile(mediaType) if (Files.exists(existingFile)) { - val parsedResponse: RdfModel = parseRdfXml(responseStr) + val parsedResponse: RdfModel = parseRdfXml(responseStr) val parsedExistingFile: RdfModel = parseRdfXml(httpGetTest.readFile(mediaType)) if (parsedResponse != parsedExistingFile) { @@ -387,14 +420,18 @@ class OntologyV2R2RSpec extends R2RSpec { } "not allow the user to request the knora-base ontology" in { - Get("/v2/ontologies/allentities/http%3A%2F%2Fapi.knora.org%2Fontology%2Fknora-base%2Fv2") ~> ontologiesPath ~> check { + Get( + "/v2/ontologies/allentities/http%3A%2F%2Fapi.knora.org%2Fontology%2Fknora-base%2Fv2" + ) ~> ontologiesPath ~> check { val responseStr: String = responseAs[String] assert(response.status == StatusCodes.BadRequest, responseStr) } } "not allow the user to request the knora-admin ontology" in { - Get("/v2/ontologies/allentities/http%3A%2F%2Fapi.knora.org%2Fontology%2Fknora-admin%2Fv2") ~> ontologiesPath ~> check { + Get( + "/v2/ontologies/allentities/http%3A%2F%2Fapi.knora.org%2Fontology%2Fknora-admin%2Fv2" + ) ~> ontologiesPath ~> check { val responseStr: String = responseAs[String] assert(response.status == StatusCodes.BadRequest, responseStr) } @@ -405,16 +442,16 @@ class OntologyV2R2RSpec extends R2RSpec { val params = s"""{ - | "knora-api:ontologyName": "foo", - | "knora-api:attachedToProject": { - | "@id": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}" - | }, - | "rdfs:label": "$label", - | "@context": { - | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - | "knora-api": "http://api.knora.org/ontology/knora-api/v2#" - | } - |}""".stripMargin + | "knora-api:ontologyName": "foo", + | "knora-api:attachedToProject": { + | "@id": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}" + | }, + | "rdfs:label": "$label", + | "@context": { + | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + | "knora-api": "http://api.knora.org/ontology/knora-api/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -428,12 +465,13 @@ class OntologyV2R2RSpec extends R2RSpec { ) Post("/v2/ontologies", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == "http://0.0.0.0:3333/ontology/0001/foo/v2") fooIri.set(ontologyIri) assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString(label)) @@ -460,22 +498,22 @@ class OntologyV2R2RSpec extends R2RSpec { } "create an empty ontology called 'bar' with a comment" in { - val label = "The bar ontology" + val label = "The bar ontology" val comment = "some comment" val params = s"""{ - | "knora-api:ontologyName": "bar", - | "knora-api:attachedToProject": { - | "@id": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}" - | }, - | "rdfs:label": "$label", - | "rdfs:comment": "$comment", - | "@context": { - | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - | "knora-api": "http://api.knora.org/ontology/knora-api/v2#" - | } - |}""".stripMargin + | "knora-api:ontologyName": "bar", + | "knora-api:attachedToProject": { + | "@id": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}" + | }, + | "rdfs:label": "$label", + | "rdfs:comment": "$comment", + | "@context": { + | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + | "knora-api": "http://api.knora.org/ontology/knora-api/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -489,16 +527,19 @@ class OntologyV2R2RSpec extends R2RSpec { ) Post("/v2/ontologies", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == "http://0.0.0.0:3333/ontology/0001/bar/v2") assert( metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString( - stringFormatter.fromSparqlEncodedString(comment))) + stringFormatter.fromSparqlEncodedString(comment) + ) + ) barIri.set(ontologyIri) val lastModDate = metadata.requireDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.LastModificationDate, @@ -521,7 +562,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "create an empty ontology called 'test' with a comment that has a special character" in { - val label = "The test ontology" + val label = "The test ontology" val comment = "some \\\"test\\\" comment" val params = @@ -539,38 +580,41 @@ class OntologyV2R2RSpec extends R2RSpec { |}""".stripMargin Post("/v2/ontologies", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == "http://0.0.0.0:3333/ontology/0001/test/v2") assert( metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString( - stringFormatter.fromSparqlEncodedString(comment))) + stringFormatter.fromSparqlEncodedString(comment) + ) + ) } } "change the metadata of 'foo'" in { - val newLabel = "The modified foo ontology" + val newLabel = "The modified foo ontology" val newComment = "new comment" val params = s"""{ - | "@id": "${fooIri.get}", - | "rdfs:label": "$newLabel", - | "rdfs:comment": "$newComment", - | "knora-api:lastModificationDate": { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$fooLastModDate" - | }, - | "@context": { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#" - | } - |}""".stripMargin + | "@id": "${fooIri.get}", + | "rdfs:label": "$newLabel", + | "rdfs:comment": "$newComment", + | "knora-api:lastModificationDate": { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$fooLastModDate" + | }, + | "@context": { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -584,11 +628,12 @@ class OntologyV2R2RSpec extends R2RSpec { ) Put("/v2/ontologies/metadata", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == fooIri.get) assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString(newLabel)) assert(metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString(newComment)) @@ -623,15 +668,18 @@ class OntologyV2R2RSpec extends R2RSpec { |}""".stripMargin Put("/v2/ontologies/metadata", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == barIri.get) assert( metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString( - stringFormatter.fromSparqlEncodedString(newComment))) + stringFormatter.fromSparqlEncodedString(newComment) + ) + ) val lastModDate = metadata.requireDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.LastModificationDate, @@ -645,15 +693,16 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the comment from 'foo'" in { - val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") + val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") val lastModificationDate = URLEncoder.encode(fooLastModDate.toString, "UTF-8") Delete(s"/v2/ontologies/comment/$fooIriEncoded?lastModificationDate=$lastModificationDate") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == fooIri.get) assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString("The modified foo ontology")) assert(!metadata.value.contains(OntologyConstants.Rdfs.Comment)) @@ -673,7 +722,8 @@ class OntologyV2R2RSpec extends R2RSpec { val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") Get(s"/v2/ontologies/candeleteontology/$fooIriEncoded") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] clientTestDataCollector.addFile( @@ -694,11 +744,12 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the 'foo' ontology" in { - val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") + val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") val lastModificationDate = URLEncoder.encode(fooLastModDate.toString, "UTF-8") Delete(s"/v2/ontologies/$fooIriEncoded?lastModificationDate=$lastModificationDate") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) @@ -718,55 +769,55 @@ class OntologyV2R2RSpec extends R2RSpec { "create a property anything:hasName as a subproperty of knora-api:hasValue and schema:name" in { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:hasName", - | "@type" : "owl:ObjectProperty", - | "knora-api:subjectType" : { - | "@id" : "anything:Thing" - | }, - | "knora-api:objectType" : { - | "@id" : "knora-api:TextValue" - | }, - | "rdfs:comment" : [ { - | "@language" : "en", - | "@value" : "The name of a Thing" - | }, { - | "@language" : "de", - | "@value" : "Der Name eines Dinges" - | } ], - | "rdfs:label" : [ { - | "@language" : "en", - | "@value" : "has name" - | }, { - | "@language" : "de", - | "@value" : "hat Namen" - | } ], - | "rdfs:subPropertyOf" : [ { - | "@id" : "knora-api:hasValue" - | }, { - | "@id" : "http://schema.org/name" - | } ], - | "salsah-gui:guiElement" : { - | "@id" : "salsah-gui:SimpleText" - | }, - | "salsah-gui:guiAttribute" : [ "size=80", "maxlength=100" ] - | } ], - | "@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 + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:hasName", + | "@type" : "owl:ObjectProperty", + | "knora-api:subjectType" : { + | "@id" : "anything:Thing" + | }, + | "knora-api:objectType" : { + | "@id" : "knora-api:TextValue" + | }, + | "rdfs:comment" : [ { + | "@language" : "en", + | "@value" : "The name of a Thing" + | }, { + | "@language" : "de", + | "@value" : "Der Name eines Dinges" + | } ], + | "rdfs:label" : [ { + | "@language" : "en", + | "@value" : "has name" + | }, { + | "@language" : "de", + | "@value" : "hat Namen" + | } ], + | "rdfs:subPropertyOf" : [ { + | "@id" : "knora-api:hasValue" + | }, { + | "@id" : "http://schema.org/name" + | } ], + | "salsah-gui:guiElement" : { + | "@id" : "salsah-gui:SimpleText" + | }, + | "salsah-gui:guiAttribute" : [ "size=80", "maxlength=100" ] + | } ], + | "@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 clientTestDataCollector.addFile( TestDataFileContent( @@ -783,7 +834,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Post("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -814,35 +866,35 @@ class OntologyV2R2RSpec extends R2RSpec { "change the rdfs:label of a property" in { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:hasName", - | "@type" : "owl:ObjectProperty", - | "rdfs:label" : [ { - | "@language" : "en", - | "@value" : "has name" - | }, { - | "@language" : "fr", - | "@value" : "a nom" - | }, { - | "@language" : "de", - | "@value" : "hat Namen" - | } ] - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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 + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:hasName", + | "@type" : "owl:ObjectProperty", + | "rdfs:label" : [ { + | "@language" : "en", + | "@value" : "has name" + | }, { + | "@language" : "fr", + | "@value" : "a nom" + | }, { + | "@language" : "de", + | "@value" : "hat Namen" + | } ] + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( TestDataFileContent( @@ -859,7 +911,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Put("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -867,7 +920,8 @@ class OntologyV2R2RSpec extends R2RSpec { val responseAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape responseAsInput.properties.head._2.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects.toSet should ===( - paramsAsInput.properties.head._2.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects.toSet) + paramsAsInput.properties.head._2.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects.toSet + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -879,35 +933,35 @@ class OntologyV2R2RSpec extends R2RSpec { "change the rdfs:comment of a property" in { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:hasName", - | "@type" : "owl:ObjectProperty", - | "rdfs:comment" : [ { - | "@language" : "en", - | "@value" : "The name of a Thing" - | }, { - | "@language" : "fr", - | "@value" : "Le nom d'une chose" - | }, { - | "@language" : "de", - | "@value" : "Der Name eines Dinges" - | } ] - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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 + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:hasName", + | "@type" : "owl:ObjectProperty", + | "rdfs:comment" : [ { + | "@language" : "en", + | "@value" : "The name of a Thing" + | }, { + | "@language" : "fr", + | "@value" : "Le nom d'une chose" + | }, { + | "@language" : "de", + | "@value" : "Der Name eines Dinges" + | } ] + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( TestDataFileContent( @@ -924,7 +978,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Put("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -935,7 +990,8 @@ class OntologyV2R2RSpec extends R2RSpec { .predicates(OntologyConstants.Rdfs.Comment.toSmartIri) .objects .toSet should ===( - paramsAsInput.properties.head._2.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects.toSet) + paramsAsInput.properties.head._2.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects.toSet + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -947,40 +1003,41 @@ class OntologyV2R2RSpec extends R2RSpec { "add an rdfs:comment to a link property that has no rdfs:comment" in { val params = s"""{ - | "@id": "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type": "owl:Ontology", - | "knora-api:lastModificationDate": { - | "@type": "xsd:dateTimeStamp", - | "@value": "$anythingLastModDate" - | }, - | "@graph": [ - | { - | "@id": "anything:hasBlueThing", - | "@type": "owl:ObjectProperty", - | "rdfs:comment": [ - | { - | "@language": "en", - | "@value": "asdas asd as dasdasdas" - | } - | ] - | } - | ], - | "@context": { - | "anything": "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}#", - | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - | "owl": "http://www.w3.org/2002/07/owl#", - | "xsd": "http://www.w3.org/2001/XMLSchema#", - | "knora-api": "http://api.knora.org/ontology/knora-api/v2#", - | "salsah-gui": "http://api.knora.org/ontology/salsah-gui/v2#" - | } - |}""".stripMargin + | "@id": "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type": "owl:Ontology", + | "knora-api:lastModificationDate": { + | "@type": "xsd:dateTimeStamp", + | "@value": "$anythingLastModDate" + | }, + | "@graph": [ + | { + | "@id": "anything:hasBlueThing", + | "@type": "owl:ObjectProperty", + | "rdfs:comment": [ + | { + | "@language": "en", + | "@value": "asdas asd as dasdasdas" + | } + | ] + | } + | ], + | "@context": { + | "anything": "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}#", + | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + | "owl": "http://www.w3.org/2002/07/owl#", + | "xsd": "http://www.w3.org/2001/XMLSchema#", + | "knora-api": "http://api.knora.org/ontology/knora-api/v2#", + | "salsah-gui": "http://api.knora.org/ontology/salsah-gui/v2#" + | } + |}""".stripMargin // Convert the submitted JSON-LD to an InputOntologyV2, without SPARQL-escaping, so we can compare it to the response. val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Put("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -988,7 +1045,8 @@ class OntologyV2R2RSpec extends R2RSpec { val responseAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape responseAsInput.properties.head._2.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( - paramsAsInput.properties.head._2.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects) + paramsAsInput.properties.head._2.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1017,7 +1075,7 @@ class OntologyV2R2RSpec extends R2RSpec { | "@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#", + | "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#", @@ -1039,8 +1097,10 @@ class OntologyV2R2RSpec extends R2RSpec { // Convert the submitted JSON-LD to an InputOntologyV2, without SPARQL-escaping, so we can compare it to the response. val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape - Put("/v2/ontologies/properties/guielement", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Put( + "/v2/ontologies/properties/guielement", + HttpEntity(RdfMediaTypes.`application/ld+json`, params) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1056,7 +1116,8 @@ class OntologyV2R2RSpec extends R2RSpec { paramsAsInput.properties.head._2 .predicates(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri) .objects - .toSet) + .toSet + ) responseAsInput.properties.head._2 .predicates(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri) @@ -1065,7 +1126,8 @@ class OntologyV2R2RSpec extends R2RSpec { paramsAsInput.properties.head._2 .predicates(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri) .objects - .toSet) + .toSet + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1109,8 +1171,10 @@ class OntologyV2R2RSpec extends R2RSpec { ) ) - Put("/v2/ontologies/properties/guielement", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Put( + "/v2/ontologies/properties/guielement", + HttpEntity(RdfMediaTypes.`application/ld+json`, params) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1121,11 +1185,13 @@ class OntologyV2R2RSpec extends R2RSpec { assert( !responseAsInput.properties.head._2.predicates - .contains(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri)) + .contains(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri) + ) assert( !responseAsInput.properties.head._2.predicates - .contains(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri)) + .contains(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri) + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1138,47 +1204,47 @@ class OntologyV2R2RSpec extends R2RSpec { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:WildThing", - | "@type" : "owl:Class", - | "rdfs:label" : { - | "@language" : "en", - | "@value" : "wild thing" - | }, - | "rdfs:comment" : { - | "@language" : "en", - | "@value" : "A thing that is wild" - | }, - | "rdfs:subClassOf" : [ - | { - | "@id": "anything:Thing" - | }, - | { - | "@type": "http://www.w3.org/2002/07/owl#Restriction", - | "owl:maxCardinality": 1, - | "owl:onProperty": { - | "@id": "anything:hasName" - | }, - | "salsah-gui:guiOrder": 1 - | } - | ] - | } ], - | "@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" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:WildThing", + | "@type" : "owl:Class", + | "rdfs:label" : { + | "@language" : "en", + | "@value" : "wild thing" + | }, + | "rdfs:comment" : { + | "@language" : "en", + | "@value" : "A thing that is wild" + | }, + | "rdfs:subClassOf" : [ + | { + | "@id": "anything:Thing" + | }, + | { + | "@type": "http://www.w3.org/2002/07/owl#Restriction", + | "owl:maxCardinality": 1, + | "owl:onProperty": { + | "@id": "anything:hasName" + | }, + | "salsah-gui:guiOrder": 1 + | } + | ] + | } ], + | "@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" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1195,7 +1261,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Post("/v2/ontologies/classes", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1206,7 +1273,8 @@ class OntologyV2R2RSpec extends R2RSpec { // Check that cardinalities were inherited from anything:Thing. getPropertyIrisFromResourceClassResponse(responseJsonDoc) should contain( - "http://0.0.0.0:3333/ontology/0001/anything/v2#hasDecimal".toSmartIri) + "http://0.0.0.0:3333/ontology/0001/anything/v2#hasDecimal".toSmartIri + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1218,37 +1286,37 @@ class OntologyV2R2RSpec extends R2RSpec { "create a class anything:Nothing with no properties" in { val params = s""" - |{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:Nothing", - | "@type" : "owl:Class", - | "rdfs:label" : { - | "@language" : "en", - | "@value" : "nothing" - | }, - | "rdfs:comment" : { - | "@language" : "en", - | "@value" : "Represents nothing" - | }, - | "rdfs:subClassOf" : { - | "@id" : "knora-api:Resource" - | } - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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}#" - | } - |} + |{ + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:Nothing", + | "@type" : "owl:Class", + | "rdfs:label" : { + | "@language" : "en", + | "@value" : "nothing" + | }, + | "rdfs:comment" : { + | "@language" : "en", + | "@value" : "Represents nothing" + | }, + | "rdfs:subClassOf" : { + | "@id" : "knora-api:Resource" + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( @@ -1266,7 +1334,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Post("/v2/ontologies/classes", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -1278,7 +1347,8 @@ class OntologyV2R2RSpec extends R2RSpec { // Check that cardinalities were inherited from knora-api:Resource. getPropertyIrisFromResourceClassResponse(responseJsonDoc) should contain( - "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri) + "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1301,32 +1371,32 @@ class OntologyV2R2RSpec extends R2RSpec { "change the labels of a class" in { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:Nothing", - | "@type" : "owl:Class", - | "rdfs:label" : [ { - | "@language" : "en", - | "@value" : "nothing" - | }, { - | "@language" : "fr", - | "@value" : "rien" - | } ] - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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 + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:Nothing", + | "@type" : "owl:Class", + | "rdfs:label" : [ { + | "@language" : "en", + | "@value" : "nothing" + | }, { + | "@language" : "fr", + | "@value" : "rien" + | } ] + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( TestDataFileContent( @@ -1343,7 +1413,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Put("/v2/ontologies/classes", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1351,7 +1422,8 @@ class OntologyV2R2RSpec extends R2RSpec { val responseAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape responseAsInput.classes.head._2.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( - paramsAsInput.classes.head._2.predicates.head._2.objects) + paramsAsInput.classes.head._2.predicates.head._2.objects + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1363,49 +1435,50 @@ class OntologyV2R2RSpec extends R2RSpec { "change the comments of a class" in { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:Nothing", - | "@type" : "owl:Class", - | "rdfs:comment" : [ { - | "@language" : "en", - | "@value" : "Represents nothing" - | }, { - | "@language" : "fr", - | "@value" : "ne représente rien" - | } ] - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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 - - clientTestDataCollector.addFile( - TestDataFileContent( - filePath = TestDataFilePath( - directoryPath = clientTestDataPath, - filename = "change-class-comment-request", - fileExtension = "json" - ), - text = params - ) - ) - - // Convert the submitted JSON-LD to an InputOntologyV2, without SPARQL-escaping, so we can compare it to the response. - val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape - - Put("/v2/ontologies/classes", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:Nothing", + | "@type" : "owl:Class", + | "rdfs:comment" : [ { + | "@language" : "en", + | "@value" : "Represents nothing" + | }, { + | "@language" : "fr", + | "@value" : "ne représente rien" + | } ] + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 + + clientTestDataCollector.addFile( + TestDataFileContent( + filePath = TestDataFilePath( + directoryPath = clientTestDataPath, + filename = "change-class-comment-request", + fileExtension = "json" + ), + text = params + ) + ) + + // Convert the submitted JSON-LD to an InputOntologyV2, without SPARQL-escaping, so we can compare it to the response. + val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape + + Put("/v2/ontologies/classes", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1413,7 +1486,8 @@ class OntologyV2R2RSpec extends R2RSpec { val responseAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape responseAsInput.classes.head._2.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( - paramsAsInput.classes.head._2.predicates.head._2.objects) + paramsAsInput.classes.head._2.predicates.head._2.objects + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1426,43 +1500,43 @@ class OntologyV2R2RSpec extends R2RSpec { val params = s""" - |{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:hasOtherNothing", - | "@type" : "owl:ObjectProperty", - | "knora-api:subjectType" : { - | "@id" : "anything:Nothing" - | }, - | "knora-api:objectType" : { - | "@id" : "anything:Nothing" - | }, - | "rdfs:comment" : { - | "@language" : "en", - | "@value" : "Refers to the other Nothing of a Nothing" - | }, - | "rdfs:label" : { - | "@language" : "en", - | "@value" : "has nothingness" - | }, - | "rdfs:subPropertyOf" : { - | "@id" : "knora-api:hasLinkTo" - | } - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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}#" - | } - |} + |{ + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:hasOtherNothing", + | "@type" : "owl:ObjectProperty", + | "knora-api:subjectType" : { + | "@id" : "anything:Nothing" + | }, + | "knora-api:objectType" : { + | "@id" : "anything:Nothing" + | }, + | "rdfs:comment" : { + | "@language" : "en", + | "@value" : "Refers to the other Nothing of a Nothing" + | }, + | "rdfs:label" : { + | "@language" : "en", + | "@value" : "has nothingness" + | }, + | "rdfs:subPropertyOf" : { + | "@id" : "knora-api:hasLinkTo" + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( @@ -1480,7 +1554,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Post("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -1511,33 +1586,33 @@ class OntologyV2R2RSpec extends R2RSpec { "add a cardinality for the property anything:hasOtherNothing to the class anything:Nothing" in { val params = s""" - |{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:Nothing", - | "@type" : "owl:Class", - | "rdfs:subClassOf" : { - | "@type": "owl:Restriction", - | "owl:maxCardinality" : 1, - | "owl:onProperty" : { - | "@id" : "anything:hasOtherNothing" - | } - | } - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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}#" - | } - |} + |{ + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:Nothing", + | "@type" : "owl:Class", + | "rdfs:subClassOf" : { + | "@type": "owl:Restriction", + | "owl:maxCardinality" : 1, + | "owl:onProperty" : { + | "@id" : "anything:hasOtherNothing" + | } + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( @@ -1555,19 +1630,20 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape val paramsWithAddedLinkValueCardinality = paramsAsInput.copy( - classes = paramsAsInput.classes.map { - case (classIri, classDef) => - val hasOtherNothingValueCardinality = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherNothingValue".toSmartIri -> + classes = paramsAsInput.classes.map { case (classIri, classDef) => + val hasOtherNothingValueCardinality = + "http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherNothingValue".toSmartIri -> classDef.directCardinalities("http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherNothing".toSmartIri) - classIri -> classDef.copy( - directCardinalities = classDef.directCardinalities + hasOtherNothingValueCardinality - ) + classIri -> classDef.copy( + directCardinalities = classDef.directCardinalities + hasOtherNothingValueCardinality + ) } ) Post("/v2/ontologies/cardinalities", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -1576,11 +1652,13 @@ class OntologyV2R2RSpec extends R2RSpec { val responseAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape responseAsInput.classes.head._2.directCardinalities should ===( - paramsWithAddedLinkValueCardinality.classes.head._2.directCardinalities) + paramsWithAddedLinkValueCardinality.classes.head._2.directCardinalities + ) // Check that cardinalities were inherited from knora-api:Resource. getPropertyIrisFromResourceClassResponse(responseJsonDoc) should contain( - "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri) + "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1603,25 +1681,25 @@ class OntologyV2R2RSpec extends R2RSpec { "remove the cardinality for the property anything:hasOtherNothing from the class anything:Nothing" in { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:Nothing", - | "@type" : "owl:Class" - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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 + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:Nothing", + | "@type" : "owl:Class" + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( TestDataFileContent( @@ -1635,7 +1713,8 @@ class OntologyV2R2RSpec extends R2RSpec { ) Put("/v2/ontologies/cardinalities", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1646,7 +1725,8 @@ class OntologyV2R2RSpec extends R2RSpec { // Check that cardinalities were inherited from knora-api:Resource. getPropertyIrisFromResourceClassResponse(responseJsonDoc) should contain( - "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri) + "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1659,7 +1739,8 @@ class OntologyV2R2RSpec extends R2RSpec { val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherNothing", "UTF-8") Get(s"/v2/ontologies/candeleteproperty/$propertySegment") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -1668,15 +1749,17 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the property anything:hasOtherNothing" in { - val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherNothing", "UTF-8") + val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherNothing", "UTF-8") val lastModificationDate = URLEncoder.encode(anythingLastModDate.toString, "UTF-8") - Delete(s"/v2/ontologies/properties/$propertySegment?lastModificationDate=$lastModificationDate") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Delete( + s"/v2/ontologies/properties/$propertySegment?lastModificationDate=$lastModificationDate" + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) responseJsonDoc.requireStringWithValidation("@id", stringFormatter.toSmartIriWithErr) should ===( - "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri) + "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri + ) val newAnythingLastModDate = responseJsonDoc.requireDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.LastModificationDate, @@ -1692,48 +1775,49 @@ class OntologyV2R2RSpec extends R2RSpec { "create a property anything:hasNothingness with knora-api:subjectType anything:Nothing" in { val params = s"""{ - | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:hasNothingness", - | "@type" : "owl:ObjectProperty", - | "knora-api:subjectType" : { - | "@id" : "anything:Nothing" - | }, - | "knora-api:objectType" : { - | "@id" : "knora-api:BooleanValue" - | }, - | "rdfs:comment" : { - | "@language" : "en", - | "@value" : "Indicates whether a Nothing has nothingness" - | }, - | "rdfs:label" : { - | "@language" : "en", - | "@value" : "has nothingness" - | }, - | "rdfs:subPropertyOf" : { - | "@id" : "knora-api:hasValue" - | } - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:hasNothingness", + | "@type" : "owl:ObjectProperty", + | "knora-api:subjectType" : { + | "@id" : "anything:Nothing" + | }, + | "knora-api:objectType" : { + | "@id" : "knora-api:BooleanValue" + | }, + | "rdfs:comment" : { + | "@language" : "en", + | "@value" : "Indicates whether a Nothing has nothingness" + | }, + | "rdfs:label" : { + | "@language" : "en", + | "@value" : "has nothingness" + | }, + | "rdfs:subPropertyOf" : { + | "@id" : "knora-api:hasValue" + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin // Convert the submitted JSON-LD to an InputOntologyV2, without SPARQL-escaping, so we can compare it to the response. val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Post("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1752,38 +1836,39 @@ class OntologyV2R2RSpec extends R2RSpec { "add a cardinality for the property anything:hasNothingness to the class anything:Nothing" in { val params = s"""{ - | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:Nothing", - | "@type" : "owl:Class", - | "rdfs:subClassOf" : { - | "@type": "owl:Restriction", - | "owl:maxCardinality" : 1, - | "owl:onProperty" : { - | "@id" : "anything:hasNothingness" - | } - | } - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:Nothing", + | "@type" : "owl:Class", + | "rdfs:subClassOf" : { + | "@type": "owl:Restriction", + | "owl:maxCardinality" : 1, + | "owl:onProperty" : { + | "@id" : "anything:hasNothingness" + | } + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin // Convert the submitted JSON-LD to an InputOntologyV2, without SPARQL-escaping, so we can compare it to the response. val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Post("/v2/ontologies/cardinalities", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1791,11 +1876,13 @@ class OntologyV2R2RSpec extends R2RSpec { val responseAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape responseAsInput.classes.head._2.directCardinalities should ===( - paramsAsInput.classes.head._2.directCardinalities) + paramsAsInput.classes.head._2.directCardinalities + ) // Check that cardinalities were inherited from knora-api:Resource. getPropertyIrisFromResourceClassResponse(responseJsonDoc) should contain( - "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri) + "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1822,7 +1909,7 @@ class OntologyV2R2RSpec extends R2RSpec { | "owl:onProperty": { | "@id" : "anything:hasNothingness" | }, - | "salsah-gui:guiOrder": 2 + | "salsah-gui:guiOrder": 2 | } | } ], | "@context" : { @@ -1851,7 +1938,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Put("/v2/ontologies/guiorder", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1859,7 +1947,8 @@ class OntologyV2R2RSpec extends R2RSpec { val responseAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape responseAsInput.classes.head._2.directCardinalities should ===( - paramsAsInput.classes.head._2.directCardinalities) + paramsAsInput.classes.head._2.directCardinalities + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -1871,48 +1960,49 @@ class OntologyV2R2RSpec extends R2RSpec { "create a property anything:hasEmptiness with knora-api:subjectType anything:Nothing" in { val params = s"""{ - | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:hasEmptiness", - | "@type" : "owl:ObjectProperty", - | "knora-api:subjectType" : { - | "@id" : "anything:Nothing" - | }, - | "knora-api:objectType" : { - | "@id" : "knora-api:BooleanValue" - | }, - | "rdfs:comment" : { - | "@language" : "en", - | "@value" : "Indicates whether a Nothing has emptiness" - | }, - | "rdfs:label" : { - | "@language" : "en", - | "@value" : "has emptiness" - | }, - | "rdfs:subPropertyOf" : { - | "@id" : "knora-api:hasValue" - | } - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:hasEmptiness", + | "@type" : "owl:ObjectProperty", + | "knora-api:subjectType" : { + | "@id" : "anything:Nothing" + | }, + | "knora-api:objectType" : { + | "@id" : "knora-api:BooleanValue" + | }, + | "rdfs:comment" : { + | "@language" : "en", + | "@value" : "Indicates whether a Nothing has emptiness" + | }, + | "rdfs:label" : { + | "@language" : "en", + | "@value" : "has emptiness" + | }, + | "rdfs:subPropertyOf" : { + | "@id" : "knora-api:hasValue" + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin // Convert the submitted JSON-LD to an InputOntologyV2, without SPARQL-escaping, so we can compare it to the response. val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Post("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1932,7 +2022,8 @@ class OntologyV2R2RSpec extends R2RSpec { val classSegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#Nothing", "UTF-8") Get(s"/v2/ontologies/canreplacecardinalities/$classSegment") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -1943,32 +2034,32 @@ class OntologyV2R2RSpec extends R2RSpec { "change the cardinalities of the class anything:Nothing, replacing anything:hasNothingness with anything:hasEmptiness" in { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:Nothing", - | "@type" : "owl:Class", - | "rdfs:subClassOf" : { - | "@type": "owl:Restriction", - | "owl:maxCardinality": 1, - | "owl:onProperty": { - | "@id" : "anything:hasEmptiness" - | } - | } - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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 + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:Nothing", + | "@type" : "owl:Class", + | "rdfs:subClassOf" : { + | "@type": "owl:Restriction", + | "owl:maxCardinality": 1, + | "owl:onProperty": { + | "@id" : "anything:hasEmptiness" + | } + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( TestDataFileContent( @@ -1985,7 +2076,8 @@ class OntologyV2R2RSpec extends R2RSpec { val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape Put("/v2/ontologies/cardinalities", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -1993,11 +2085,13 @@ class OntologyV2R2RSpec extends R2RSpec { val responseAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape responseAsInput.classes.head._2.directCardinalities should ===( - paramsAsInput.classes.head._2.directCardinalities) + paramsAsInput.classes.head._2.directCardinalities + ) // Check that cardinalities were inherited from knora-api:Resource. getPropertyIrisFromResourceClassResponse(responseJsonDoc) should contain( - "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri) + "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -2007,15 +2101,17 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the property anything:hasNothingness" in { - val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasNothingness", "UTF-8") + val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasNothingness", "UTF-8") val lastModificationDate = URLEncoder.encode(anythingLastModDate.toString, "UTF-8") - Delete(s"/v2/ontologies/properties/$propertySegment?lastModificationDate=$lastModificationDate") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Delete( + s"/v2/ontologies/properties/$propertySegment?lastModificationDate=$lastModificationDate" + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) responseJsonDoc.requireStringWithValidation("@id", stringFormatter.toSmartIriWithErr) should ===( - "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri) + "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri + ) val newAnythingLastModDate = responseJsonDoc.requireDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.LastModificationDate, @@ -2031,25 +2127,25 @@ class OntologyV2R2RSpec extends R2RSpec { "remove all cardinalities from the class anything:Nothing" in { val params = s"""{ - | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$anythingLastModDate" - | }, - | "@graph" : [ { - | "@id" : "anything:Nothing", - | "@type" : "owl:Class" - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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 + | "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$anythingLastModDate" + | }, + | "@graph" : [ { + | "@id" : "anything:Nothing", + | "@type" : "owl:Class" + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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 clientTestDataCollector.addFile( TestDataFileContent( @@ -2063,7 +2159,8 @@ class OntologyV2R2RSpec extends R2RSpec { ) Put("/v2/ontologies/cardinalities", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -2074,7 +2171,8 @@ class OntologyV2R2RSpec extends R2RSpec { // Check that cardinalities were inherited from knora-api:Resource. getPropertyIrisFromResourceClassResponse(responseJsonDoc) should contain( - "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri) + "http://api.knora.org/ontology/knora-api/v2#attachedToUser".toSmartIri + ) // Check that the ontology's last modification date was updated. val newAnythingLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get @@ -2084,15 +2182,17 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the property anything:hasEmptiness" in { - val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasEmptiness", "UTF-8") + val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasEmptiness", "UTF-8") val lastModificationDate = URLEncoder.encode(anythingLastModDate.toString, "UTF-8") - Delete(s"/v2/ontologies/properties/$propertySegment?lastModificationDate=$lastModificationDate") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Delete( + s"/v2/ontologies/properties/$propertySegment?lastModificationDate=$lastModificationDate" + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) responseJsonDoc.requireStringWithValidation("@id", stringFormatter.toSmartIriWithErr) should ===( - "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri) + "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri + ) val newAnythingLastModDate = responseJsonDoc.requireDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.LastModificationDate, @@ -2109,7 +2209,8 @@ class OntologyV2R2RSpec extends R2RSpec { val classSegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#Nothing", "UTF-8") Get(s"/v2/ontologies/candeleteclass/$classSegment") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2118,15 +2219,17 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the class anything:Nothing" in { - val classSegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#Nothing", "UTF-8") + val classSegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#Nothing", "UTF-8") val lastModificationDate = URLEncoder.encode(anythingLastModDate.toString, "UTF-8") Delete(s"/v2/ontologies/classes/$classSegment?lastModificationDate=$lastModificationDate") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) responseJsonDoc.requireStringWithValidation("@id", stringFormatter.toSmartIriWithErr) should ===( - "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri) + "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri + ) val newAnythingLastModDate = responseJsonDoc.requireDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.LastModificationDate, @@ -2144,26 +2247,27 @@ class OntologyV2R2RSpec extends R2RSpec { val createOntologyJson = s""" - |{ - | "knora-api:ontologyName": "useless", - | "knora-api:attachedToProject": { - | "@id": "${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}" - | }, - | "knora-api:isShared": true, - | "rdfs:label": "$label", - | "@context": { - | "rdfs": "${OntologyConstants.Rdfs.RdfsPrefixExpansion}", - | "knora-api": "${OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion}" - | } - |} + |{ + | "knora-api:ontologyName": "useless", + | "knora-api:attachedToProject": { + | "@id": "${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}" + | }, + | "knora-api:isShared": true, + | "rdfs:label": "$label", + | "@context": { + | "rdfs": "${OntologyConstants.Rdfs.RdfsPrefixExpansion}", + | "knora-api": "${OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion}" + | } + |} """.stripMargin Post("/v2/ontologies", HttpEntity(RdfMediaTypes.`application/ld+json`, createOntologyJson)) ~> addCredentials( - BasicHttpCredentials(superUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(superUsername, password) + ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == "http://api.knora.org/ontology/shared/useless/v2") uselessIri.set(ontologyIri) assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString(label)) @@ -2179,48 +2283,50 @@ class OntologyV2R2RSpec extends R2RSpec { val createPropertyJson = s""" - |{ - | "@id" : "${uselessIri.get}", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$uselessLastModDate" - | }, - | "@graph" : [ { - | "@id" : "useless:hasSharedName", - | "@type" : "owl:ObjectProperty", - | "knora-api:objectType" : { - | "@id" : "knora-api:TextValue" - | }, - | "rdfs:comment" : { - | "@language" : "en", - | "@value" : "Represents a name" - | }, - | "rdfs:label" : { - | "@language" : "en", - | "@value" : "has shared name" - | }, - | "rdfs:subPropertyOf" : { - | "@id" : "knora-api:hasValue" - | } - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/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#", - | "useless" : "${uselessIri.get}#" - | } - |} + |{ + | "@id" : "${uselessIri.get}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$uselessLastModDate" + | }, + | "@graph" : [ { + | "@id" : "useless:hasSharedName", + | "@type" : "owl:ObjectProperty", + | "knora-api:objectType" : { + | "@id" : "knora-api:TextValue" + | }, + | "rdfs:comment" : { + | "@language" : "en", + | "@value" : "Represents a name" + | }, + | "rdfs:label" : { + | "@language" : "en", + | "@value" : "has shared name" + | }, + | "rdfs:subPropertyOf" : { + | "@id" : "knora-api:hasValue" + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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#", + | "useless" : "${uselessIri.get}#" + | } + |} """.stripMargin // Convert the submitted JSON-LD to an InputOntologyV2, without SPARQL-escaping, so we can compare it to the response. val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(createPropertyJson)).unescape - Post("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, createPropertyJson)) ~> addCredentials( - BasicHttpCredentials(superUsername, password)) ~> ontologiesPath ~> check { + Post( + "/v2/ontologies/properties", + HttpEntity(RdfMediaTypes.`application/ld+json`, createPropertyJson) + ) ~> addCredentials(BasicHttpCredentials(superUsername, password)) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -2275,8 +2381,10 @@ class OntologyV2R2RSpec extends R2RSpec { | } |}""".stripMargin - Post("/v2/ontologies/classes", HttpEntity(RdfMediaTypes.`application/ld+json`, createClassRequestJson)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Post( + "/v2/ontologies/classes", + HttpEntity(RdfMediaTypes.`application/ld+json`, createClassRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) @@ -2313,7 +2421,7 @@ class OntologyV2R2RSpec extends R2RSpec { | "@language" : "en", | "@value" : "test text property" | }, - | "rdfs:subPropertyOf" : { + | "rdfs:subPropertyOf" : { | "@id" : "knora-api:hasValue" | } | } ], @@ -2328,8 +2436,10 @@ class OntologyV2R2RSpec extends R2RSpec { | } |}""".stripMargin - Post("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, createTestTextPropRequestJson)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Post( + "/v2/ontologies/properties", + HttpEntity(RdfMediaTypes.`application/ld+json`, createTestTextPropRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2382,9 +2492,10 @@ class OntologyV2R2RSpec extends R2RSpec { | } |}""".stripMargin - Post("/v2/ontologies/properties", - HttpEntity(RdfMediaTypes.`application/ld+json`, createTestIntegerPropRequestJson)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Post( + "/v2/ontologies/properties", + HttpEntity(RdfMediaTypes.`application/ld+json`, createTestIntegerPropRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2436,8 +2547,10 @@ class OntologyV2R2RSpec extends R2RSpec { | } |}""".stripMargin - Post("/v2/ontologies/properties", HttpEntity(RdfMediaTypes.`application/ld+json`, createTestLinkPropRequestJson)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Post( + "/v2/ontologies/properties", + HttpEntity(RdfMediaTypes.`application/ld+json`, createTestLinkPropRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2467,15 +2580,15 @@ class OntologyV2R2RSpec extends R2RSpec { | "owl:onProperty" : { | "@id" : "anything:testTextProp" | } - | }, - | { + | }, + | { | "@type": "owl:Restriction", | "owl:maxCardinality" : 1, | "owl:onProperty" : { | "@id" : "anything:testIntProp" | } - | }, - | { + | }, + | { | "@type": "owl:Restriction", | "owl:maxCardinality" : 1, | "owl:onProperty" : { @@ -2494,8 +2607,10 @@ class OntologyV2R2RSpec extends R2RSpec { |} """.stripMargin - Post("/v2/ontologies/cardinalities", HttpEntity(RdfMediaTypes.`application/ld+json`, addCardinalitiesRequestJson)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Post( + "/v2/ontologies/cardinalities", + HttpEntity(RdfMediaTypes.`application/ld+json`, addCardinalitiesRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2524,7 +2639,7 @@ class OntologyV2R2RSpec extends R2RSpec { | "owl:onProperty" : { | "@id" : "anything:testTextProp" | } - | }, + | }, | { | "@type": "owl:Restriction", | "owl:maxCardinality" : 1, @@ -2545,7 +2660,8 @@ class OntologyV2R2RSpec extends R2RSpec { """.stripMargin Put("/v2/ontologies/cardinalities", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2557,11 +2673,223 @@ class OntologyV2R2RSpec extends R2RSpec { } } + "create a class with two cardinalities, use one in data, and allow only removal of the cardinality for the property not used in data" in { + // Create a class with no cardinalities. + + val createClassRequestJson = CreateClassRequest + .make( + ontologyName = "freetest", + lastModificationDate = freetestLastModDate, + className = "BlueFreeTestClass", + label = LangString("en", "A Blue Free Test class"), + comment = LangString("en", "A Blue Free Test class used for testing cardinalities") + ) + .value + + Post( + "/v2/ontologies/classes", + HttpEntity(RdfMediaTypes.`application/ld+json`, createClassRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + assert(status == StatusCodes.OK, response.toString) + val responseJsonDoc = responseToJsonLDDocument(response) + + val responseAsInput: InputOntologyV2 = + InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape + + freetestLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get + } + + // Create a text property. + + val createTestTextPropRequestJson = + CreatePropertyRequest + .make( + ontologyName = "freetest", + lastModificationDate = freetestLastModDate, + propertyName = "hasBlueTestTextProp", + subjectClassName = "BlueFreeTestClass", + propertyType = PropertyValueType.TextValue, + label = LangString("en", "blue test text property"), + comment = LangString("en", "A blue test text property") + ) + .value + + Post( + "/v2/ontologies/properties", + HttpEntity(RdfMediaTypes.`application/ld+json`, createTestTextPropRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + val responseStr = responseAs[String] + assert(status == StatusCodes.OK, responseStr) + val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) + + val responseAsInput: InputOntologyV2 = + InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape + freetestLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get + + } + + // Create an integer property. + + val createTestIntegerPropRequestJson = CreatePropertyRequest + .make( + ontologyName = "freetest", + lastModificationDate = freetestLastModDate, + propertyName = "hasBlueTestIntProp", + subjectClassName = "BlueFreeTestClass", + propertyType = PropertyValueType.IntValue, + label = LangString("en", "blue test integer property"), + comment = LangString("en", "A blue test integer property") + ) + .value + + Post( + "/v2/ontologies/properties", + HttpEntity(RdfMediaTypes.`application/ld+json`, createTestIntegerPropRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + val responseStr = responseAs[String] + assert(status == StatusCodes.OK, responseStr) + val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) + + val responseAsInput: InputOntologyV2 = + InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape + freetestLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get + } + + // Add cardinalities to the class. + + val addCardinalitiesRequestJson = AddCardinalitiesRequest + .make( + ontologyName = "freetest", + lastModificationDate = freetestLastModDate, + className = "BlueFreeTestClass", + restrictions = List( + Restriction( + CardinalityRestriction.MaxCardinalityOne, + onProperty = Property(ontology = "freetest", property = "hasBlueTestTextProp") + ), + Restriction( + CardinalityRestriction.MaxCardinalityOne, + onProperty = Property(ontology = "freetest", property = "hasBlueTestIntProp") + ) + ) + ) + .value + + Post( + "/v2/ontologies/cardinalities", + HttpEntity(RdfMediaTypes.`application/ld+json`, addCardinalitiesRequestJson) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + val responseStr = responseAs[String] + assert(status == StatusCodes.OK, responseStr) + val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) + + val responseAsInput: InputOntologyV2 = + InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape + freetestLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get + } + + // Create a resource of #BlueTestClass using only #hasBlueTestIntProp + + val createResourceWithValues: String = + s"""{ + | "@type" : "freetest:BlueFreeTestClass", + | "freetest:hasBlueTestIntProp" : { + | "@type" : "knora-api:IntValue", + | "knora-api:hasPermissions" : "CR knora-admin:Creator|V http://rdfh.ch/groups/0001/thing-searcher", + | "knora-api:intValueAsInt" : 5, + | "knora-api:valueHasComment" : "this is the number five" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "my blue test class thing instance", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "freetest" : "${SharedOntologyTestDataADM.FREETEST_ONTOLOGY_IRI_LocalHost}#" + | } + |}""".stripMargin + + Post( + "/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, createResourceWithValues) + ) ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> resourcesPath ~> check { + val responseStr = responseAs[String] + assert(status == StatusCodes.OK, responseStr) + val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) + val resourceIri: IRI = + responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) + assert(resourceIri.toSmartIri.isKnoraDataIri) + } + + // Prepare the JsonLD payload to check if a cardinality can be deleted and + // then to also actually delete it. + val params = + s""" + |{ + | "@id" : "${SharedOntologyTestDataADM.FREETEST_ONTOLOGY_IRI_LocalHost}", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$freetestLastModDate" + | }, + | "@graph" : [ { + | "@id" : "freetest:BlueFreeTestClass", + | "@type" : "owl:Class", + | "rdfs:subClassOf" : { + | "@type": "owl:Restriction", + | "owl:maxCardinality" : 1, + | "owl:onProperty" : { + | "@id" : "freetest:hasBlueTestTextProp" + | } + | } + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/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#", + | "freetest" : "${SharedOntologyTestDataADM.FREETEST_ONTOLOGY_IRI_LocalHost}#" + | } + |} + """.stripMargin + + // Successfully check if the cardinality can be deleted + Post( + "/v2/ontologies/candeletecardinalities", + HttpEntity(RdfMediaTypes.`application/ld+json`, params) + ) ~> addCredentials( + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { + val responseStr = responseAs[String] + assert(status == StatusCodes.OK, response.toString) + val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) + assert(responseJsonDoc.body.value(OntologyConstants.KnoraApiV2Complex.CanDo).asInstanceOf[JsonLDBoolean].value) + } + + // Successfully remove the (unused) text value cardinality from the class. + Delete("/v2/ontologies/cardinalities", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { + val responseStr = responseAs[String] + assert(status == StatusCodes.OK, response.toString) + val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) + + val responseAsInput: InputOntologyV2 = + InputOntologyV2.fromJsonLD(responseJsonDoc, parsingMode = TestResponseParsingModeV2).unescape + freetestLastModDate = responseAsInput.ontologyMetadata.lastModificationDate.get + } + } + "determine that a class's cardinalities cannot be changed" in { val classSegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing", "UTF-8") Get(s"/v2/ontologies/canreplacecardinalities/$classSegment") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2572,7 +2900,9 @@ class OntologyV2R2RSpec extends R2RSpec { "determine that a class cannot be deleted" in { val thingIri = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing", "UTF-8") - Get(s"/v2/ontologies/candeleteclass/$thingIri") ~> addCredentials(BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + Get(s"/v2/ontologies/candeleteclass/$thingIri") ~> addCredentials( + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2584,7 +2914,8 @@ class OntologyV2R2RSpec extends R2RSpec { val propertyIri = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger", "UTF-8") Get(s"/v2/ontologies/candeleteproperty/$propertyIri") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) @@ -2596,7 +2927,8 @@ class OntologyV2R2RSpec extends R2RSpec { val ontologyIri = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2", "UTF-8") Get(s"/v2/ontologies/candeleteontology/$ontologyIri") ~> addCredentials( - BasicHttpCredentials(anythingUsername, password)) ~> ontologiesPath ~> check { + BasicHttpCredentials(anythingUsername, password) + ) ~> ontologiesPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala index e4dceffe2c..b44377874e 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala @@ -49,8 +49,8 @@ import spray.json.JsValue import scala.concurrent.duration._ /** - * Static data for testing [[ResourcesResponderV1]]. - */ + * Static data for testing [[ResourcesResponderV1]]. + */ object ResourcesResponderV1Spec { private val config: Config = ConfigFactory.parseString(""" akka.loglevel = "DEBUG" @@ -63,7 +63,8 @@ object ResourcesResponderV1Spec { id = "http://rdfh.ch/0803/2a6221216701", value = Vector("Reise ins Heilige Land", "Reysen und wanderschafften durch das Gelobte Land", "Itinerarius"), rights = Some(6) - )) + ) + ) ) private val ReiseInsHeiligelandOneValueRestrictedToBook: ResourceSearchResponseV1 = ResourceSearchResponseV1( @@ -72,7 +73,8 @@ object ResourcesResponderV1Spec { id = "http://rdfh.ch/0803/2a6221216701", value = Vector("Reise ins Heilige Land"), rights = Some(6) - )) + ) + ) ) private val propertiesGetResponseV1Region = PropertiesGetResponseV1( @@ -87,13 +89,16 @@ object ResourcesResponderV1Spec { "", "0", Vector( - PropertyGetValueV1(None, - None, - "Siehe Seite c5v", - TextValueSimpleV1("Siehe Seite c5v"), - "http://rdfh.ch/0803/021ec18f1735/values/8a96c303338201", - None, - None)) + PropertyGetValueV1( + None, + None, + "Siehe Seite c5v", + TextValueSimpleV1("Siehe Seite c5v"), + "http://rdfh.ch/0803/021ec18f1735/values/8a96c303338201", + None, + None + ) + ) ), PropertyGetV1( "http://www.knora.org/ontology/knora-base#hasColor", @@ -104,13 +109,16 @@ object ResourcesResponderV1Spec { "ncolors=8", "0", Vector( - PropertyGetValueV1(None, - None, - "#ff3333", - ColorValueV1("#ff3333"), - "http://rdfh.ch/0803/021ec18f1735/values/10ea6976338201", - None, - None)) + PropertyGetValueV1( + None, + None, + "#ff3333", + ColorValueV1("#ff3333"), + "http://rdfh.ch/0803/021ec18f1735/values/10ea6976338201", + None, + None + ) + ) ), PropertyGetV1( "http://www.knora.org/ontology/knora-base#hasGeometry", @@ -120,16 +128,19 @@ object ResourcesResponderV1Spec { Some("geometry"), "", "0", - Vector(PropertyGetValueV1( - None, - None, - "{\"status\":\"active\",\"lineColor\":\"#ff3333\",\"lineWidth\":2,\"points\":[{\"x\":0.08098591549295775,\"y\":0.16741071428571427},{\"x\":0.7394366197183099,\"y\":0.7299107142857143}],\"type\":\"rectangle\",\"original_index\":0}", - GeomValueV1( - "{\"status\":\"active\",\"lineColor\":\"#ff3333\",\"lineWidth\":2,\"points\":[{\"x\":0.08098591549295775,\"y\":0.16741071428571427},{\"x\":0.7394366197183099,\"y\":0.7299107142857143}],\"type\":\"rectangle\",\"original_index\":0}"), - "http://rdfh.ch/0803/021ec18f1735/values/4dc0163d338201", - None, - None - )) + Vector( + PropertyGetValueV1( + None, + None, + "{\"status\":\"active\",\"lineColor\":\"#ff3333\",\"lineWidth\":2,\"points\":[{\"x\":0.08098591549295775,\"y\":0.16741071428571427},{\"x\":0.7394366197183099,\"y\":0.7299107142857143}],\"type\":\"rectangle\",\"original_index\":0}", + GeomValueV1( + "{\"status\":\"active\",\"lineColor\":\"#ff3333\",\"lineWidth\":2,\"points\":[{\"x\":0.08098591549295775,\"y\":0.16741071428571427},{\"x\":0.7394366197183099,\"y\":0.7299107142857143}],\"type\":\"rectangle\",\"original_index\":0}" + ), + "http://rdfh.ch/0803/021ec18f1735/values/4dc0163d338201", + None, + None + ) + ) ), PropertyGetV1( "http://www.knora.org/ontology/knora-base#isRegionOf", @@ -139,21 +150,27 @@ object ResourcesResponderV1Spec { None, "restypeid=http://www.knora.org/ontology/knora-base#Representation", "0", - Vector(PropertyGetValueV1( - None, - None, - "http://rdfh.ch/0803/9d626dc76c03", - LinkV1("http://rdfh.ch/0803/9d626dc76c03", - Some("u1r"), - Some("http://www.knora.org/ontology/0803/incunabula#page"), - None, - None), - "http://rdfh.ch/0803/021ec18f1735/values/fbcb88bf-cd16-4b7b-b843-51e17c0669d7", - None, - None - )) + Vector( + PropertyGetValueV1( + None, + None, + "http://rdfh.ch/0803/9d626dc76c03", + LinkV1( + "http://rdfh.ch/0803/9d626dc76c03", + Some("u1r"), + Some("http://www.knora.org/ontology/0803/incunabula#page"), + None, + None + ), + "http://rdfh.ch/0803/021ec18f1735/values/fbcb88bf-cd16-4b7b-b843-51e17c0669d7", + None, + None + ) + ) ) - ))) + ) + ) + ) private val hasOtherThingIncomingLink = IncomingV1( value = Some("A thing that only project members can see"), @@ -166,7 +183,8 @@ object ResourcesResponderV1Spec { preview = None, restype_iconsrc = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), restype_description = Some( - "'The whole world is full of things, which means there's a real need for someone to go searching for them. And that's exactly what a thing-searcher does.' --Pippi Longstocking"), + "'The whole world is full of things, which means there's a real need for someone to go searching for them. And that's exactly what a thing-searcher does.' --Pippi Longstocking" + ), restype_label = Some("Ding"), restype_name = Some("http://www.knora.org/ontology/0001/anything#Thing"), restype_id = "http://www.knora.org/ontology/0001/anything#Thing", @@ -190,7 +208,8 @@ object ResourcesResponderV1Spec { preview = None, restype_iconsrc = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), restype_description = Some( - "'The whole world is full of things, which means there's a real need for someone to go searching for them. And that's exactly what a thing-searcher does.' --Pippi Longstocking"), + "'The whole world is full of things, which means there's a real need for someone to go searching for them. And that's exactly what a thing-searcher does.' --Pippi Longstocking" + ), restype_label = Some("Ding"), restype_name = Some("http://www.knora.org/ontology/0001/anything#Thing"), restype_id = "http://www.knora.org/ontology/0001/anything#Thing", @@ -218,7 +237,8 @@ object ResourcesResponderV1Spec { valueResourceClass = Some("http://www.knora.org/ontology/0001/anything#Thing"), valueLabel = Some("Another thing that only project members can see"), targetResourceIri = "http://rdfh.ch/0001/project-thing-2" - )), + ) + ), occurrence = Some("0-n"), attributes = "restypeid=http://www.knora.org/ontology/0001/anything#Thing", label = Some("Ein anderes Ding"), @@ -243,7 +263,8 @@ object ResourcesResponderV1Spec { valueResourceClass = Some("http://www.knora.org/ontology/0001/anything#Thing"), valueLabel = Some("Another thing that only project members can see"), targetResourceIri = "http://rdfh.ch/0001/project-thing-2" - )), + ) + ), occurrence = Some("0-n"), attributes = "restypeid=http://www.knora.org/ontology/knora-base#Resource", label = Some("hat Standoff Link zu"), @@ -598,7 +619,8 @@ object ResourcesResponderV1Spec { propertyIri = "http://www.knora.org/ontology/knora-base#hasStandoffLinkTo", target = "http://rdfh.ch/0001/a-thing", source = "http://rdfh.ch/0001/a-thing-with-text-values" - )), + ) + ), nodes = Vector( GraphNodeV1( resourceClassLabel = "Ding", @@ -623,13 +645,14 @@ object ResourcesResponderV1Spec { resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", resourceLabel = "Another thing", resourceIri = "http://rdfh.ch/0001/another-thing" - )) + ) + ) ) } /** - * Tests [[ResourcesResponderV1]]. - */ + * Tests [[ResourcesResponderV1]]. + */ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) with ImplicitSender { import ResourcesResponderV1Spec._ @@ -646,7 +669,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) /* we need to run our app with the mocked sipi actor */ override lazy val appActor: ActorRef = system.actorOf( Props(new ApplicationActor with ManagersWithMockedSipi).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = APPLICATION_MANAGER_ACTOR_NAME) + name = APPLICATION_MANAGER_ACTOR_NAME + ) // The default timeout for receiving reply messages from actors. private val timeout = 60.seconds @@ -671,27 +695,28 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) .toSource(sortedReceivedProps)}" ) - sortedExpectedProps.zip(sortedReceivedProps).foreach { - case (expectedProp: PropertyV1, receivedProp: PropertyV1) => - // sort property attributes - val expectedPropWithSortedAttr = expectedProp.copy( - attributes = expectedProp.attributes.toSeq.sorted.unwrap - ) + sortedExpectedProps.zip(sortedReceivedProps).foreach { case (expectedProp: PropertyV1, receivedProp: PropertyV1) => + // sort property attributes + val expectedPropWithSortedAttr = expectedProp.copy( + attributes = expectedProp.attributes.toSeq.sorted.unwrap + ) - val receivedPropWithSortedAttr = receivedProp.copy( - attributes = receivedProp.attributes.toSeq.sorted.unwrap - ) + val receivedPropWithSortedAttr = receivedProp.copy( + attributes = receivedProp.attributes.toSeq.sorted.unwrap + ) - assert( - receivedPropWithSortedAttr == expectedPropWithSortedAttr, - s"These props do not match:\n********** Expected:\n${MessageUtil - .toSource(expectedProp)}\n********** Received:\n${MessageUtil.toSource(receivedProp)}" - ) + assert( + receivedPropWithSortedAttr == expectedPropWithSortedAttr, + s"These props do not match:\n********** Expected:\n${MessageUtil + .toSource(expectedProp)}\n********** Received:\n${MessageUtil.toSource(receivedProp)}" + ) } } - private def compareResourcePartOfContextResponses(received: ResourceContextResponseV1, - expected: ResourceContextResponseV1): Unit = { + private def compareResourcePartOfContextResponses( + received: ResourceContextResponseV1, + expected: ResourceContextResponseV1 + ): Unit = { val receivedContext = received.resource_context val expectedContext = expected.resource_context @@ -702,33 +727,36 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) assert(receivedContext.parent_resinfo == expectedContext.parent_resinfo, "parent_resinfo does not match") } - private def compareResourceSearchResults(received: ResourceSearchResponseV1, - expected: ResourceSearchResponseV1): Unit = { - + private def compareResourceSearchResults( + received: ResourceSearchResponseV1, + expected: ResourceSearchResponseV1 + ): Unit = assert(received.resources == expected.resources, "resources did not match") - } private def checkResourceCreation(received: ResourceCreateResponseV1, expected: Map[IRI, Seq[ApiValueV1]]): Unit = { // sort values by their string representation val sortedValuesReceived: Map[IRI, Seq[ResourceCreateValueResponseV1]] = received.results.map { case (propIri, propValues: Seq[ResourceCreateValueResponseV1]) => - (propIri, propValues.sortBy { valueObject: ResourceCreateValueResponseV1 => - val stringValue = valueObject.value.textval.map { - case (valType: LiteralValueType.Value, value: String) => value // get string and ignore value type - }.head // each value is represented by a map consisting of only one item (e.g. string -> "book title") - stringValue - }) + ( + propIri, + propValues.sortBy { valueObject: ResourceCreateValueResponseV1 => + val stringValue = valueObject.value.textval.map { case (valType: LiteralValueType.Value, value: String) => + value // get string and ignore value type + }.head // each value is represented by a map consisting of only one item (e.g. string -> "book title") + stringValue + } + ) } // sort values by their string representation - val sortedValuesExpected: Map[IRI, Seq[ResourceCreateValueResponseV1]] = expected - .map { - case (propIri, propValues) => (propIri, propValues.sortBy(_.toString)) - } - .map { - // turn the expected ApiValueV1s in ResourceCreateValueResponseV1 (these are returned by the actor). - case (propIri: IRI, propValues: Seq[ApiValueV1]) => - (propIri, propValues.map { propValue: ApiValueV1 => + val sortedValuesExpected: Map[IRI, Seq[ResourceCreateValueResponseV1]] = expected.map { + case (propIri, propValues) => (propIri, propValues.sortBy(_.toString)) + }.map { + // turn the expected ApiValueV1s in ResourceCreateValueResponseV1 (these are returned by the actor). + case (propIri: IRI, propValues: Seq[ApiValueV1]) => + ( + propIri, + propValues.map { propValue: ApiValueV1 => val valueResponse = CreateValueResponseV1( value = propValue, rights = 6, @@ -742,27 +770,27 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) propertyIri = propIri, valueResponse = valueResponse ) - }) - } + } + ) + } // compare expected and received values - sortedValuesExpected.foreach { - case (propIri, propValuesExpected) => - assert(propValuesExpected.size == sortedValuesReceived(propIri).size, "number of props did not match") - - (propValuesExpected, sortedValuesReceived(propIri)).zipped.foreach { - case (expected: ResourceCreateValueResponseV1, received: ResourceCreateValueResponseV1) => - assert(received.value.textval == expected.value.textval, "textval did not match") - assert(received.value.ival == expected.value.ival, "ival did not match") - assert(received.value.dval == expected.value.dval, "dval did not match") - assert(received.value.dateval1 == expected.value.dateval1, "dateval1 did not match") - assert(received.value.dateval2 == expected.value.dateval2, "dateval2 did not match") - assert(received.value.calendar == expected.value.calendar, "calendar did not match") - assert(received.value.dateprecision1 == expected.value.dateprecision1, "dateprecision1 did not match") - assert(received.value.dateprecision2 == expected.value.dateprecision2, "dateprecision2 did not match") - assert(received.value.timeval1 == expected.value.timeval1, "timeval1 did not match") - assert(received.value.timeval2 == expected.value.timeval2, "timeval2 did not match") - } + sortedValuesExpected.foreach { case (propIri, propValuesExpected) => + assert(propValuesExpected.size == sortedValuesReceived(propIri).size, "number of props did not match") + + propValuesExpected.lazyZip(sortedValuesReceived(propIri)).foreach { + case (expected: ResourceCreateValueResponseV1, received: ResourceCreateValueResponseV1) => + assert(received.value.textval == expected.value.textval, "textval did not match") + assert(received.value.ival == expected.value.ival, "ival did not match") + assert(received.value.dval == expected.value.dval, "dval did not match") + assert(received.value.dateval1 == expected.value.dateval1, "dateval1 did not match") + assert(received.value.dateval2 == expected.value.dateval2, "dateval2 did not match") + assert(received.value.calendar == expected.value.calendar, "calendar did not match") + assert(received.value.dateprecision1 == expected.value.dateprecision1, "dateprecision1 did not match") + assert(received.value.dateprecision2 == expected.value.dateprecision2, "dateprecision2 did not match") + assert(received.value.timeval1 == expected.value.timeval1, "timeval1 did not match") + assert(received.value.timeval2 == expected.value.timeval2, "timeval2 did not match") + } } } @@ -777,51 +805,52 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) storeManager ! SparqlSelectRequest(lastModSparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - assert(rows.size <= 1, s"Resource $resourceIri has more than one instance of knora-base:lastModificationDate") - - if (rows.size == 1) { - Some(rows.head.rowMap("lastModificationDate")) - } else { - None - } + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + assert(rows.size <= 1, s"Resource $resourceIri has more than one instance of knora-base:lastModificationDate") + + if (rows.size == 1) { + Some(rows.head.rowMap("lastModificationDate")) + } else { + None + } } } - private def comparePropertiesGetResponse(received: PropertiesGetResponseV1, - expected: PropertiesGetResponseV1): Unit = { + private def comparePropertiesGetResponse( + received: PropertiesGetResponseV1, + expected: PropertiesGetResponseV1 + ): Unit = { - assert(received.properties.properties.length == expected.properties.properties.length, - "The length of given properties is not correct.") + assert( + received.properties.properties.length == expected.properties.properties.length, + "The length of given properties is not correct." + ) - expected.properties.properties - .sortBy { - // sort by property Iri - prop => - prop.pid - } + expected.properties.properties.sortBy { + // sort by property Iri + prop => + prop.pid + } .zip(received.properties.properties.sortBy { prop => prop.pid }) - .foreach { - case (expectedProp: PropertyGetV1, receivedProp: PropertyGetV1) => - // sort the values of each property - val expectedPropValuesSorted = expectedProp.values.sortBy(values => values.textval) + .foreach { case (expectedProp: PropertyGetV1, receivedProp: PropertyGetV1) => + // sort the values of each property + val expectedPropValuesSorted = expectedProp.values.sortBy(values => values.textval) - val receivedPropValuesSorted = receivedProp.values.sortBy(values => values.textval) + val receivedPropValuesSorted = receivedProp.values.sortBy(values => values.textval) - // create PropertyGetV1 with sorted values - val expectedPropSorted = expectedProp.copy( - values = expectedPropValuesSorted - ) + // create PropertyGetV1 with sorted values + val expectedPropSorted = expectedProp.copy( + values = expectedPropValuesSorted + ) - val receivedPropSorted = receivedProp.copy( - values = receivedPropValuesSorted - ) + val receivedPropSorted = receivedProp.copy( + values = receivedPropValuesSorted + ) - assert(receivedPropSorted == expectedPropSorted, "Property did not match") + assert(receivedPropSorted == expectedPropSorted, "Property did not match") } } @@ -872,8 +901,10 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) PermissionADM.restrictedViewPermission("http://www.knora.org/ontology/knora-admin#UnknownUser") ) - responderManager ! ObjectAccessPermissionsForResourceGetADM(resourceIri = newBookResourceIri.get, - requestingUser = KnoraSystemInstances.Users.SystemUser) + responderManager ! ObjectAccessPermissionsForResourceGetADM( + resourceIri = newBookResourceIri.get, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) expectMsgPF(timeout) { case Some(permission) => val perms = permission.asInstanceOf[ObjectAccessPermissionADM].hasPermissions @@ -886,27 +917,33 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) "The resources responder" should { "return a full description of the book 'Zeitglöcklein des Lebens und Leidens Christi' in the Incunabula test data" in { // http://0.0.0.0:3333/v1/resources/http%3A%2F%2Frdfh.ch%2F0803%2Fc5058f3a - responderManager ! ResourceFullGetRequestV1(iri = "http://rdfh.ch/0803/c5058f3a", - featureFactoryConfig = defaultFeatureFactoryConfig, - userADM = SharedTestDataADM.incunabulaMemberUser) - - expectMsgPF(timeout) { - case response: ResourceFullResponseV1 => - compareResourceFullResponses(received = response, - expected = ResourcesResponderV1SpecFullData.expectedBookResourceFullResponse) + responderManager ! ResourceFullGetRequestV1( + iri = "http://rdfh.ch/0803/c5058f3a", + featureFactoryConfig = defaultFeatureFactoryConfig, + userADM = SharedTestDataADM.incunabulaMemberUser + ) + + expectMsgPF(timeout) { case response: ResourceFullResponseV1 => + compareResourceFullResponses( + received = response, + expected = ResourcesResponderV1SpecFullData.expectedBookResourceFullResponse + ) } } "return a full description of the first page of the book 'Zeitglöcklein des Lebens und Leidens Christi' in the Incunabula test data" in { // http://0.0.0.0:3333/v1/resources/http%3A%2F%2Frdfh.ch%2F0803%2F8a0b1e75 - responderManager ! ResourceFullGetRequestV1(iri = "http://rdfh.ch/0803/8a0b1e75", - featureFactoryConfig = defaultFeatureFactoryConfig, - userADM = SharedTestDataADM.incunabulaMemberUser) - - expectMsgPF(timeout) { - case response: ResourceFullResponseV1 => - compareResourceFullResponses(received = response, - expected = ResourcesResponderV1SpecFullData.expectedPageResourceFullResponse) + responderManager ! ResourceFullGetRequestV1( + iri = "http://rdfh.ch/0803/8a0b1e75", + featureFactoryConfig = defaultFeatureFactoryConfig, + userADM = SharedTestDataADM.incunabulaMemberUser + ) + + expectMsgPF(timeout) { case response: ResourceFullResponseV1 => + compareResourceFullResponses( + received = response, + expected = ResourcesResponderV1SpecFullData.expectedPageResourceFullResponse + ) } } @@ -933,11 +970,11 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userProfile = SharedTestDataADM.incunabulaProjectAdminUser ) - expectMsgPF(timeout) { - case response: ResourceContextResponseV1 => - compareResourcePartOfContextResponses( - received = response, - expected = ResourcesResponderV1SpecContextData.expectedPageResourceContextResponse) + expectMsgPF(timeout) { case response: ResourceContextResponseV1 => + compareResourcePartOfContextResponses( + received = response, + expected = ResourcesResponderV1SpecContextData.expectedPageResourceContextResponse + ) } } @@ -951,9 +988,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userProfile = SharedTestDataADM.incunabulaMemberUser ) - expectMsgPF(timeout) { - case response: ResourceSearchResponseV1 => - compareResourceSearchResults(received = response, expected = ReiseInsHeiligelandThreeValues) + expectMsgPF(timeout) { case response: ResourceSearchResponseV1 => + compareResourceSearchResults(received = response, expected = ReiseInsHeiligelandThreeValues) } } @@ -967,9 +1003,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userProfile = SharedTestDataADM.incunabulaMemberUser ) - expectMsgPF(timeout) { - case response: ResourceSearchResponseV1 => - compareResourceSearchResults(received = response, expected = ReiseInsHeiligelandThreeValues) + expectMsgPF(timeout) { case response: ResourceSearchResponseV1 => + compareResourceSearchResults(received = response, expected = ReiseInsHeiligelandThreeValues) } } @@ -983,9 +1018,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userProfile = SharedTestDataADM.incunabulaMemberUser ) - expectMsgPF(timeout) { - case response: ResourceSearchResponseV1 => - compareResourceSearchResults(received = response, expected = ReiseInsHeiligelandOneValueRestrictedToBook) + expectMsgPF(timeout) { case response: ResourceSearchResponseV1 => + compareResourceSearchResults(received = response, expected = ReiseInsHeiligelandOneValueRestrictedToBook) } } @@ -1005,9 +1039,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) resourceTypeIri = None ) - expectMsgPF(timeout) { - case response: ResourceSearchResponseV1 => - assert(response.resources.size == 27, s"expected 27 resources") + expectMsgPF(timeout) { case response: ResourceSearchResponseV1 => + assert(response.resources.size == 27, s"expected 27 resources") } } @@ -1022,9 +1055,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) resourceTypeIri = Some("http://www.knora.org/ontology/0803/incunabula#book") ) - expectMsgPF(timeout) { - case response: ResourceSearchResponseV1 => - assert(response.resources.size == 3, s"expected 3 resources") + expectMsgPF(timeout) { case response: ResourceSearchResponseV1 => + assert(response.resources.size == 3, s"expected 3 resources") } } @@ -1039,9 +1071,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) resourceTypeIri = Some("http://www.knora.org/ontology/0803/incunabula#page") ) - expectMsgPF(timeout) { - case response: ResourceSearchResponseV1 => - assert(response.resources.size == 19, s"expected 19 resources") + expectMsgPF(timeout) { case response: ResourceSearchResponseV1 => + assert(response.resources.size == 19, s"expected 19 resources") } } @@ -1056,9 +1087,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) resourceTypeIri = Some("http://www.knora.org/ontology/knora-base#Representation") ) - expectMsgPF(timeout) { - case response: ResourceSearchResponseV1 => - assert(response.resources.size == 19, s"expected 19 resources") + expectMsgPF(timeout) { case response: ResourceSearchResponseV1 => + assert(response.resources.size == 19, s"expected 19 resources") } } @@ -1083,8 +1113,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) responderManager ! resourceCreateRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1108,7 +1138,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) val valuesToBeCreated = Map( "http://www.knora.org/ontology/0803/incunabula#hasAuthor" -> author, "http://www.knora.org/ontology/0803/incunabula#pubdate" -> pubdate.map(date => - CreateValueV1WithComment(DateUtilV1.dateValueV1ToJulianDayNumberValueV1(date), None)) + CreateValueV1WithComment(DateUtilV1.dateValueV1ToJulianDayNumberValueV1(date), None) + ) ) val resourceCreateRequest = ResourceCreateRequestV1( @@ -1123,8 +1154,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) responderManager ! resourceCreateRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1143,8 +1174,11 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) endPosition = 39, startIndex = 0, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = nonexistentIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = nonexistentIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None ) @@ -1159,7 +1193,7 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) val pubdate = DateUtilV1.createJDNValueV1FromDateString("GREGORIAN:2015-12-03") val valuesToBeCreated: Map[IRI, Seq[CreateValueV1WithComment]] = Map( - "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(CreateValueV1WithComment(title1)), + "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(CreateValueV1WithComment(title1)), "http://www.knora.org/ontology/0803/incunabula#pubdate" -> Vector(CreateValueV1WithComment(pubdate)), "http://www.knora.org/ontology/0803/incunabula#citation" -> Vector( CreateValueV1WithComment(citation1, None) @@ -1177,8 +1211,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -1204,8 +1238,11 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) startPosition = 32, endPosition = 40, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = "http://rdfh.ch/0803/c5058f3a")), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = "http://rdfh.ch/0803/c5058f3a" + ) + ), uuid = UUID.randomUUID(), originalXMLID = None, startIndex = 1 @@ -1221,14 +1258,16 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) val publoc = TextValueSimpleV1("Entenhausen") val pubdateRequest = DateUtilV1.createJDNValueV1FromDateString("GREGORIAN:2015-12-03") - val pubdateResponse = DateValueV1(dateval1 = "2015-12-03", - dateval2 = "2015-12-03", - era1 = "CE", - era2 = "CE", - calendar = KnoraCalendarV1.GREGORIAN) + val pubdateResponse = DateValueV1( + dateval1 = "2015-12-03", + dateval2 = "2015-12-03", + era1 = "CE", + era2 = "CE", + calendar = KnoraCalendarV1.GREGORIAN + ) val valuesToBeCreated: Map[IRI, Seq[CreateValueV1WithComment]] = Map( - "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(CreateValueV1WithComment(title1)), + "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(CreateValueV1WithComment(title1)), "http://www.knora.org/ontology/0803/incunabula#pubdate" -> Vector(CreateValueV1WithComment(pubdateRequest)), "http://www.knora.org/ontology/0803/incunabula#citation" -> Vector( CreateValueV1WithComment(citation4, None), @@ -1240,10 +1279,10 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ) val valuesExpected = Map( - "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(title1), - "http://www.knora.org/ontology/0803/incunabula#pubdate" -> Vector(pubdateResponse), + "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(title1), + "http://www.knora.org/ontology/0803/incunabula#pubdate" -> Vector(pubdateResponse), "http://www.knora.org/ontology/0803/incunabula#citation" -> Vector(citation3, citation1, citation4, citation2), - "http://www.knora.org/ontology/0803/incunabula#publoc" -> Vector(publoc) + "http://www.knora.org/ontology/0803/incunabula#publoc" -> Vector(publoc) ) responderManager ! ResourceCreateRequestV1( @@ -1256,10 +1295,9 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case response: ResourceCreateResponseV1 => - newBookResourceIri.set(response.res_id) - checkResourceCreation(received = response, expected = valuesExpected) + expectMsgPF(timeout) { case response: ResourceCreateResponseV1 => + newBookResourceIri.set(response.res_id) + checkResourceCreation(received = response, expected = valuesExpected) } // Check that the resource doesn't have more than one lastModificationDate. @@ -1269,18 +1307,20 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) checkPermissionsOnResource(newBookResourceIri.get) // See if we can query the resource. - responderManager ! ResourceFullGetRequestV1(iri = newBookResourceIri.get, - featureFactoryConfig = defaultFeatureFactoryConfig, - userADM = SharedTestDataADM.incunabulaProjectAdminUser) - expectMsgPF(timeout) { - case response: ResourceFullResponseV1 => () // If we got a ResourceFullResponseV1, the operation succeeded. + responderManager ! ResourceFullGetRequestV1( + iri = newBookResourceIri.get, + featureFactoryConfig = defaultFeatureFactoryConfig, + userADM = SharedTestDataADM.incunabulaProjectAdminUser + ) + expectMsgPF(timeout) { case response: ResourceFullResponseV1 => + () // If we got a ResourceFullResponseV1, the operation succeeded. } } "create an incunabula:page with a resource pointer" in { - val recto = TextValueSimpleV1("recto") + val recto = TextValueSimpleV1("recto") val origname = TextValueSimpleV1("Blatt") - val seqnum = IntegerValueV1(1) + val seqnum = IntegerValueV1(1) val fileValue = StillImageFileValueV1( internalMimeType = "image/jp2", @@ -1296,22 +1336,26 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) val valuesToBeCreated = Map( "http://www.knora.org/ontology/0803/incunabula#hasRightSideband" -> Vector( - CreateValueV1WithComment(LinkUpdateV1(targetResourceIri = "http://rdfh.ch/0803/482a33d65c36"))), - "http://www.knora.org/ontology/0803/incunabula#pagenum" -> Vector(CreateValueV1WithComment(recto)), - "http://www.knora.org/ontology/0803/incunabula#partOf" -> Vector(CreateValueV1WithComment(LinkUpdateV1(book))), + CreateValueV1WithComment(LinkUpdateV1(targetResourceIri = "http://rdfh.ch/0803/482a33d65c36")) + ), + "http://www.knora.org/ontology/0803/incunabula#pagenum" -> Vector(CreateValueV1WithComment(recto)), + "http://www.knora.org/ontology/0803/incunabula#partOf" -> Vector(CreateValueV1WithComment(LinkUpdateV1(book))), "http://www.knora.org/ontology/0803/incunabula#origname" -> Vector(CreateValueV1WithComment(origname)), - "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Vector(CreateValueV1WithComment(seqnum)) + "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Vector(CreateValueV1WithComment(seqnum)) ) val expected = Map( "http://www.knora.org/ontology/0803/incunabula#hasRightSideband" -> Vector( - LinkV1(targetResourceIri = "http://rdfh.ch/0803/482a33d65c36", - valueResourceClass = Some("http://www.knora.org/ontology/0803/incunabula#Sideband"))), - "http://www.knora.org/ontology/0803/incunabula#pagenum" -> Vector(recto), - "http://www.knora.org/ontology/0803/incunabula#partOf" -> Vector(LinkV1(book)), + LinkV1( + targetResourceIri = "http://rdfh.ch/0803/482a33d65c36", + valueResourceClass = Some("http://www.knora.org/ontology/0803/incunabula#Sideband") + ) + ), + "http://www.knora.org/ontology/0803/incunabula#pagenum" -> Vector(recto), + "http://www.knora.org/ontology/0803/incunabula#partOf" -> Vector(LinkV1(book)), "http://www.knora.org/ontology/0803/incunabula#origname" -> Vector(origname), - "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Vector(seqnum), - OntologyConstants.KnoraBase.HasStillImageFileValue -> Vector(fileValue) + "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Vector(seqnum), + OntologyConstants.KnoraBase.HasStillImageFileValue -> Vector(fileValue) ) responderManager ! ResourceCreateRequestV1( @@ -1325,10 +1369,9 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case response: ResourceCreateResponseV1 => - newPageResourceIri.set(response.res_id) - checkResourceCreation(received = response, expected = expected) + expectMsgPF(timeout) { case response: ResourceCreateResponseV1 => + newPageResourceIri.set(response.res_id) + checkResourceCreation(received = response, expected = expected) } /* Check the permissions on the resource */ @@ -1349,9 +1392,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) responderManager ! pageGetContext - expectMsgPF(timeout) { - case response: ResourceContextResponseV1 => - compareNewPageContextResponse(received = response) + expectMsgPF(timeout) { case response: ResourceContextResponseV1 => + compareNewPageContextResponse(received = response) } } @@ -1377,8 +1419,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userProfile = SharedTestDataADM.incunabulaProjectAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } // Check that the resource's last modification date got updated. @@ -1396,9 +1438,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) responderManager ! propertiesGetRequest - expectMsgPF(timeout) { - case response: PropertiesGetResponseV1 => - comparePropertiesGetResponse(received = response, expected = propertiesGetResponseV1Region) + expectMsgPF(timeout) { case response: PropertiesGetResponseV1 => + comparePropertiesGetResponse(received = response, expected = propertiesGetResponseV1Region) } } @@ -1413,8 +1454,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) responderManager ! resourceContextPage - expectMsgPF(timeout) { - case response: ResourceContextResponseV1 => comparePageContextRegionResponse(received = response) + expectMsgPF(timeout) { case response: ResourceContextResponseV1 => + comparePageContextRegionResponse(received = response) } } @@ -1427,91 +1468,97 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userADM = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case response: ResourceFullResponseV1 => - response.incoming.size should ===(2) - response.incoming.contains(hasStandoffLinkToIncomingLink) should ===(true) - response.incoming.contains(hasOtherThingIncomingLink) should ===(true) + expectMsgPF(timeout) { case response: ResourceFullResponseV1 => + response.incoming.size should ===(2) + response.incoming.contains(hasStandoffLinkToIncomingLink) should ===(true) + response.incoming.contains(hasOtherThingIncomingLink) should ===(true) } // But another user should see only the hasStandoffLinkTo link. - responderManager ! ResourceFullGetRequestV1(iri = "http://rdfh.ch/0001/project-thing-2", - featureFactoryConfig = defaultFeatureFactoryConfig, - userADM = SharedTestDataADM.anythingUser2) + responderManager ! ResourceFullGetRequestV1( + iri = "http://rdfh.ch/0001/project-thing-2", + featureFactoryConfig = defaultFeatureFactoryConfig, + userADM = SharedTestDataADM.anythingUser2 + ) - expectMsgPF(timeout) { - case response: ResourceFullResponseV1 => - response.incoming.contains(hasStandoffLinkToIncomingLink) should ===(true) - response.incoming.contains(hasOtherThingIncomingLink) should ===(false) + expectMsgPF(timeout) { case response: ResourceFullResponseV1 => + response.incoming.contains(hasStandoffLinkToIncomingLink) should ===(true) + response.incoming.contains(hasOtherThingIncomingLink) should ===(false) } } "show outgoing standoff links if the user has view permission on both resources, but show other outgoing links only if the user also has view permission on the link" in { // The link's owner, anythingUser1, should see the hasOtherThing link as well as the hasStandoffLinkTo link. - responderManager ! ResourceFullGetRequestV1(iri = "http://rdfh.ch/0001/project-thing-1", - featureFactoryConfig = defaultFeatureFactoryConfig, - userADM = SharedTestDataADM.anythingUser1) + responderManager ! ResourceFullGetRequestV1( + iri = "http://rdfh.ch/0001/project-thing-1", + featureFactoryConfig = defaultFeatureFactoryConfig, + userADM = SharedTestDataADM.anythingUser1 + ) - expectMsgPF(timeout) { - case response: ResourceFullResponseV1 => - val linkProps = response.props.get.properties.filter { prop => - prop.values.nonEmpty && prop.valuetype_id.get != OntologyConstants.KnoraBase.TextValue - } + expectMsgPF(timeout) { case response: ResourceFullResponseV1 => + val linkProps = response.props.get.properties.filter { prop => + prop.values.nonEmpty && prop.valuetype_id.get != OntologyConstants.KnoraBase.TextValue + } - linkProps.size should ===(2) + linkProps.size should ===(2) - linkProps.contains(hasStandoffLinkToOutgoingLink) should ===(true) - linkProps.contains(hasOtherThingOutgoingLink) should ===(true) + linkProps.contains(hasStandoffLinkToOutgoingLink) should ===(true) + linkProps.contains(hasOtherThingOutgoingLink) should ===(true) } // But another user should see only the hasStandoffLinkTo link. - responderManager ! ResourceFullGetRequestV1(iri = "http://rdfh.ch/0001/project-thing-1", - featureFactoryConfig = defaultFeatureFactoryConfig, - userADM = SharedTestDataADM.anythingUser2) + responderManager ! ResourceFullGetRequestV1( + iri = "http://rdfh.ch/0001/project-thing-1", + featureFactoryConfig = defaultFeatureFactoryConfig, + userADM = SharedTestDataADM.anythingUser2 + ) - expectMsgPF(timeout) { - case response: ResourceFullResponseV1 => - val linkProps = response.props.get.properties.filter { prop => - prop.values.nonEmpty && prop.valuetype_id.get != OntologyConstants.KnoraBase.TextValue - } + expectMsgPF(timeout) { case response: ResourceFullResponseV1 => + val linkProps = response.props.get.properties.filter { prop => + prop.values.nonEmpty && prop.valuetype_id.get != OntologyConstants.KnoraBase.TextValue + } - linkProps.size should ===(1) - linkProps.contains(hasStandoffLinkToOutgoingLink) should ===(true) - linkProps.contains(hasOtherThingOutgoingLink) should ===(false) + linkProps.size should ===(1) + linkProps.contains(hasStandoffLinkToOutgoingLink) should ===(true) + linkProps.contains(hasOtherThingOutgoingLink) should ===(false) } } "show a contained resource in a context request only if the user has permission to see the containing resource, the contained resource, and the link value" in { // The owner of the resources and the link should see two contained resources. - responderManager ! ResourceContextGetRequestV1(iri = "http://rdfh.ch/0001/containing-thing", - resinfo = true, - featureFactoryConfig = defaultFeatureFactoryConfig, - userProfile = SharedTestDataADM.anythingUser1) - - expectMsgPF(timeout) { - case response: ResourceContextResponseV1 => - response.resource_context.res_id should ===( - Some( - Vector( - "http://rdfh.ch/0001/contained-thing-1", - "http://rdfh.ch/0001/contained-thing-2" - ))) + responderManager ! ResourceContextGetRequestV1( + iri = "http://rdfh.ch/0001/containing-thing", + resinfo = true, + featureFactoryConfig = defaultFeatureFactoryConfig, + userProfile = SharedTestDataADM.anythingUser1 + ) + + expectMsgPF(timeout) { case response: ResourceContextResponseV1 => + response.resource_context.res_id should ===( + Some( + Vector( + "http://rdfh.ch/0001/contained-thing-1", + "http://rdfh.ch/0001/contained-thing-2" + ) + ) + ) } // Another user in the project, who doesn't have permission to see the second link, should see only one contained resource. - responderManager ! ResourceContextGetRequestV1(iri = "http://rdfh.ch/0001/containing-thing", - resinfo = true, - featureFactoryConfig = defaultFeatureFactoryConfig, - userProfile = SharedTestDataADM.anythingUser2) + responderManager ! ResourceContextGetRequestV1( + iri = "http://rdfh.ch/0001/containing-thing", + resinfo = true, + featureFactoryConfig = defaultFeatureFactoryConfig, + userProfile = SharedTestDataADM.anythingUser2 + ) - expectMsgPF(timeout) { - case response: ResourceContextResponseV1 => - response.resource_context.res_id should ===(Some(Vector("http://rdfh.ch/0001/contained-thing-1"))) + expectMsgPF(timeout) { case response: ResourceContextResponseV1 => + response.resource_context.res_id should ===(Some(Vector("http://rdfh.ch/0001/contained-thing-1"))) } // A user who's not in the project shouldn't see any contained resources. @@ -1523,9 +1570,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userProfile = SharedTestDataADM.incunabulaProjectAdminUser ) - expectMsgPF(timeout) { - case response: ResourceContextResponseV1 => - response.resource_context.res_id should ===(None) + expectMsgPF(timeout) { case response: ResourceContextResponseV1 => + response.resource_context.res_id should ===(None) } } @@ -1541,8 +1587,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1558,8 +1604,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1575,8 +1621,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1591,9 +1637,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case response: ChangeResourceLabelResponseV1 => - response.label should ===(myNewLabel) + expectMsgPF(timeout) { case response: ChangeResourceLabelResponseV1 => + response.label should ===(myNewLabel) } } @@ -1601,7 +1646,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) "not create an anything:BlueThing with property anything:hasBlueThing pointing to an anything:Thing" in { val valuesToBeCreated = Map( "http://www.knora.org/ontology/0001/anything#hasBlueThing" -> Vector( - CreateValueV1WithComment(LinkUpdateV1(targetResourceIri = "http://rdfh.ch/0001/a-thing"))) + CreateValueV1WithComment(LinkUpdateV1(targetResourceIri = "http://rdfh.ch/0001/a-thing")) + ) ) responderManager ! ResourceCreateRequestV1( @@ -1615,9 +1661,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1629,8 +1674,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ) val response = expectMsgType[GraphDataGetResponseV1](timeout) - val edges = response.edges - val nodes = response.nodes + val edges = response.edges + val nodes = response.nodes edges should contain theSameElementsAs graphForAnythingUser1.edges nodes should contain theSameElementsAs graphForAnythingUser1.nodes @@ -1644,8 +1689,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ) val response = expectMsgType[GraphDataGetResponseV1](timeout) - val edges = response.edges - val nodes = response.nodes + val edges = response.edges + val nodes = response.nodes edges should contain theSameElementsAs graphForIncunabulaUser.edges nodes should contain theSameElementsAs graphForIncunabulaUser.nodes @@ -1658,8 +1703,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userADM = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case response: GraphDataGetResponseV1 => response should ===(graphWithStandoffLink) + expectMsgPF(timeout) { case response: GraphDataGetResponseV1 => + response should ===(graphWithStandoffLink) } } @@ -1670,8 +1715,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) userADM = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case response: GraphDataGetResponseV1 => response should ===(graphWithOneNode) + expectMsgPF(timeout) { case response: GraphDataGetResponseV1 => + response should ===(graphWithOneNode) } } } 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 8f06c9de66..0c4772f1b9 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 @@ -19,9 +19,6 @@ package org.knora.webapi.responders.v2 -import java.time.Instant -import java.util.UUID - import akka.testkit.ImplicitSender import org.knora.webapi._ import org.knora.webapi.exceptions._ @@ -29,54 +26,73 @@ import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.messages.util.KnoraSystemInstances import org.knora.webapi.messages.util.rdf.SparqlSelectResult -import org.knora.webapi.messages.v2.responder.SuccessResponseV2 +import org.knora.webapi.messages.v2.responder.{CanDoResponseV2, SuccessResponseV2} import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.KnoraCardinalityInfo import org.knora.webapi.messages.v2.responder.ontologymessages._ +import org.knora.webapi.messages.v2.responder.resourcemessages.{ + CreateResourceRequestV2, + CreateResourceV2, + CreateValueInNewResourceV2, + ReadResourcesSequenceV2 +} +import org.knora.webapi.messages.v2.responder.valuemessages.IntegerValueContentV2 import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.MutableTestIri +import java.time.Instant +import java.util.UUID import scala.concurrent.duration._ import scala.language.postfixOps /** - * Tests [[OntologyResponderV2]]. - */ + * Tests [[OntologyResponderV2]]. + */ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - private val imagesUser = SharedTestDataADM.imagesUser01 + private val imagesUser = SharedTestDataADM.imagesUser01 private val imagesProjectIri = SharedTestDataADM.IMAGES_PROJECT_IRI.toSmartIri - private val anythingAdminUser = SharedTestDataADM.anythingAdminUser + private val anythingAdminUser = SharedTestDataADM.anythingAdminUser private val anythingNonAdminUser = SharedTestDataADM.anythingUser1 - private val anythingProjectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI.toSmartIri + private val anythingProjectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI.toSmartIri - private val exampleSharedOntology = RdfDataObject(path = "test_data/ontologies/example-box.ttl", - name = "http://www.knora.org/ontology/shared/example-box") + private val exampleSharedOntology = RdfDataObject( + path = "test_data/ontologies/example-box.ttl", + name = "http://www.knora.org/ontology/shared/example-box" + ) private val anythingData = RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") + private val freeTestOntology = + RdfDataObject(path = "test_data/ontologies/freetest-onto.ttl", name = "http://www.knora.org/ontology/0001/freetest") + + private val freeTestData = + RdfDataObject(path = "test_data/all_data/freetest-data.ttl", name = "http://www.knora.org/data/0001/freetest") + // The default timeout for receiving reply messages from actors. private val timeout = 10.seconds - private val fooIri = new MutableTestIri + private val fooIri = new MutableTestIri private var fooLastModDate: Instant = Instant.now - private val barIri = new MutableTestIri + private val barIri = new MutableTestIri private var barLastModDate: Instant = Instant.now - private val chairIri = new MutableTestIri + private val chairIri = new MutableTestIri private var chairLastModDate: Instant = Instant.now - 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 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 var anythingLastModDate: Instant = Instant.parse("2017-12-19T15:23:42.166Z") + private var freetestLastModData: Instant = Instant.parse("2012-12-12T12:12:12.12Z") private val printErrorMessages = false - override lazy val rdfDataObjects: Seq[RdfDataObject] = List(exampleSharedOntology, anythingData) + override lazy val rdfDataObjects: Seq[RdfDataObject] = + List(exampleSharedOntology, anythingData, freeTestOntology, freeTestData) private def loadInvalidTestData(rdfDataObjs: List[RdfDataObject]): Unit = { storeManager ! ResetRepositoryContent(rdfDataObjs) @@ -102,10 +118,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.imagesUser02 ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -125,7 +140,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.ontologyIri.toString == "http://www.knora.org/ontology/00FF/foo") fooIri.set(metadata.ontologyIri.toString) fooLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) } "change the label in the metadata of 'foo'" in { @@ -146,7 +162,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.ontologyIri == fooIri.get.toSmartIri) assert(metadata.label.contains(newLabel)) val newFooLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) assert(newFooLastModDate.isAfter(fooLastModDate)) fooLastModDate = newFooLastModDate } @@ -169,13 +186,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.ontologyIri == fooIri.get.toSmartIri) assert(metadata.comment.contains(aComment)) val newFooLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) assert(newFooLastModDate.isAfter(fooLastModDate)) fooLastModDate = newFooLastModDate } "change both the label and the comment of the 'foo' ontology" in { - val aLabel = "a changed label" + val aLabel = "a changed label" val aComment = "a changed comment" responderManager ! ChangeOntologyMetadataRequestV2( @@ -195,7 +213,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.label.contains(aLabel)) assert(metadata.comment.contains(aComment)) val newFooLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) assert(newFooLastModDate.isAfter(fooLastModDate)) fooLastModDate = newFooLastModDate } @@ -219,7 +238,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.label.contains(newLabel)) assert(metadata.comment.contains("a changed comment")) val newFooLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) assert(newFooLastModDate.isAfter(fooLastModDate)) fooLastModDate = newFooLastModDate } @@ -240,7 +260,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.label.contains("a label changed again")) assert(metadata.comment.isEmpty) val newFooLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) assert(newFooLastModDate.isAfter(fooLastModDate)) fooLastModDate = newFooLastModDate } @@ -256,9 +277,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -282,7 +302,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(returnedComment == "some comment") barIri.set(metadata.ontologyIri.toString) barLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) } "change the existing comment in the metadata of 'bar' ontology" in { @@ -303,7 +324,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.ontologyIri == barIri.get.toSmartIri) assert(metadata.comment.contains(newComment)) val newBarLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) assert(newBarLastModDate.isAfter(barLastModDate)) barLastModDate = newBarLastModDate } @@ -318,10 +340,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -334,10 +355,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -350,10 +370,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.imagesUser02 ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -401,7 +420,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) val metadataResponse = expectMsgType[ReadOntologyMetadataV2](timeout) - assert(metadataResponse.ontologies.size == 2) + assert(metadataResponse.ontologies.size == 3) anythingLastModDate = metadataResponse .toOntologySchema(ApiV2Complex) .ontologies @@ -418,21 +437,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - val cause: Throwable = msg.cause - val errorMsg: String = cause.getMessage - if (printErrorMessages) println(errorMsg) - cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + val cause: Throwable = msg.cause + val errorMsg: String = cause.getMessage + if (printErrorMessages) println(errorMsg) + cause.isInstanceOf[BadRequestException] should ===(true) - val expectedSubjects = Set( - "", // rdf:type anything:Thing - "", // rdf:type anything:BlueThing, a subclass of anything:Thing - "", // a subclass of anything:Thing in another ontology - "" // a subproperty of anything:hasOtherThing in another ontology - ) + val expectedSubjects = Set( + "", // rdf:type anything:Thing + "", // rdf:type anything:BlueThing, a subclass of anything:Thing + "", // a subclass of anything:Thing in another ontology + "" // a subproperty of anything:hasOtherThing in another ontology + ) - expectedSubjects.forall(s => errorMsg.contains(s)) should ===(true) + expectedSubjects.forall(s => errorMsg.contains(s)) should ===(true) } } @@ -446,10 +464,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -464,10 +481,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -482,10 +498,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -500,10 +515,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -518,10 +532,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -536,10 +549,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -554,10 +566,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -572,10 +583,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -591,10 +601,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = imagesUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -608,10 +617,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.superUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -632,7 +640,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.ontologyIri.toString == "http://www.knora.org/ontology/shared/chair") chairIri.set(metadata.ontologyIri.toOntologySchema(ApiV2Complex).toString) chairLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) } "not allow a user to create a property if they are not a sysadmin or an admin in the ontology's project" in { @@ -643,7 +652,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) val metadataResponse = expectMsgType[ReadOntologyMetadataV2](timeout) - assert(metadataResponse.ontologies.size == 2) + assert(metadataResponse.ontologies.size == 3) anythingLastModDate = metadataResponse .toOntologySchema(ApiV2Complex) .ontologies @@ -697,10 +706,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -712,7 +720,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) val metadataResponse = expectMsgType[ReadOntologyMetadataV2](timeout) - assert(metadataResponse.ontologies.size == 2) + assert(metadataResponse.ontologies.size == 3) anythingLastModDate = metadataResponse .toOntologySchema(ApiV2Complex) .ontologies @@ -766,16 +774,16 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - val property = externalOntology.properties(propertyIri) - property.entityInfoContent should ===(propertyInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + val property = externalOntology.properties(propertyIri) + property.entityInfoContent should ===(propertyInfoContent) + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Reload the ontology cache and see if we get the same result. @@ -793,12 +801,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val readPropertyInfo: ReadPropertyInfoV2 = externalOntology.properties.values.head - readPropertyInfo.entityInfoContent should ===(propertyInfoContent) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo: ReadPropertyInfoV2 = externalOntology.properties.values.head + readPropertyInfo.entityInfoContent should ===(propertyInfoContent) } } @@ -810,7 +817,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) val metadataResponse = expectMsgType[ReadOntologyMetadataV2](timeout) - assert(metadataResponse.ontologies.size == 2) + assert(metadataResponse.ontologies.size == 3) anythingLastModDate = metadataResponse .toOntologySchema(ApiV2Complex) .ontologies @@ -861,18 +868,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - val property = externalOntology.properties(propertyIri) - assert(property.isLinkProp) - assert(!property.isLinkValueProp) - externalOntology.properties(propertyIri).entityInfoContent should ===(propertyInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + val property = externalOntology.properties(propertyIri) + assert(property.isLinkProp) + assert(!property.isLinkValueProp) + externalOntology.properties(propertyIri).entityInfoContent should ===(propertyInfoContent) + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Check that the link value property was created. @@ -885,14 +892,13 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val readPropertyInfo: ReadPropertyInfoV2 = externalOntology.properties.values.head - assert(readPropertyInfo.entityInfoContent.propertyIri == linkValuePropIri) - assert(!readPropertyInfo.isLinkProp) - assert(readPropertyInfo.isLinkValueProp) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo: ReadPropertyInfoV2 = externalOntology.properties.values.head + assert(readPropertyInfo.entityInfoContent.propertyIri == linkValuePropIri) + assert(!readPropertyInfo.isLinkProp) + assert(readPropertyInfo.isLinkValueProp) } // Reload the ontology cache and see if we get the same result. @@ -910,14 +916,13 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val readPropertyInfo: ReadPropertyInfoV2 = externalOntology.properties.values.head - assert(readPropertyInfo.isLinkProp) - assert(!readPropertyInfo.isLinkValueProp) - readPropertyInfo.entityInfoContent should ===(propertyInfoContent) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo: ReadPropertyInfoV2 = externalOntology.properties.values.head + assert(readPropertyInfo.isLinkProp) + assert(!readPropertyInfo.isLinkValueProp) + readPropertyInfo.entityInfoContent should ===(propertyInfoContent) } responderManager ! PropertiesGetRequestV2( @@ -926,14 +931,13 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val readPropertyInfo: ReadPropertyInfoV2 = externalOntology.properties.values.head - assert(readPropertyInfo.entityInfoContent.propertyIri == linkValuePropIri) - assert(!readPropertyInfo.isLinkProp) - assert(readPropertyInfo.isLinkValueProp) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo: ReadPropertyInfoV2 = externalOntology.properties.values.head + assert(readPropertyInfo.entityInfoContent.propertyIri == linkValuePropIri) + assert(!readPropertyInfo.isLinkProp) + assert(readPropertyInfo.isLinkValueProp) } } @@ -978,10 +982,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1029,10 +1032,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1080,10 +1082,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1131,10 +1132,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1182,10 +1182,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1236,10 +1235,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1287,10 +1285,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1311,8 +1308,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ), OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri -> PredicateInfoV2( predicateIri = OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri, - objects = Seq(SmartIriLiteralV2( - (OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion + "NonexistentClass").toSmartIri)) + objects = Seq( + SmartIriLiteralV2( + (OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion + "NonexistentClass").toSmartIri + ) + ) ), OntologyConstants.Rdfs.Label.toSmartIri -> PredicateInfoV2( predicateIri = OntologyConstants.Rdfs.Label.toSmartIri, @@ -1339,10 +1339,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1390,10 +1389,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1441,10 +1439,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1492,10 +1489,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1543,10 +1539,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1594,10 +1589,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1645,10 +1639,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1696,10 +1689,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1747,10 +1739,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1798,10 +1789,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1850,10 +1840,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1910,10 +1899,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1969,10 +1957,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1996,10 +1983,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -2023,19 +2009,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val readPropertyInfo = externalOntology.properties(propertyIri) - readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( - newObjects) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo = externalOntology.properties(propertyIri) + readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( + newObjects + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2058,19 +2045,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val readPropertyInfo = externalOntology.properties(propertyIri) - readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( - newObjects) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo = externalOntology.properties(propertyIri) + readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( + newObjects + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2080,7 +2068,10 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { val newObjects = Seq( StringLiteralV2("The name of a Thing", Some("en")), - StringLiteralV2("Le nom d\\'une chose", Some("fr")), // This is SPARQL-escaped as it would be if taken from a JSON-LD request. + StringLiteralV2( + "Le nom d\\'une chose", + Some("fr") + ), // This is SPARQL-escaped as it would be if taken from a JSON-LD request. StringLiteralV2("Der Name eines Dinges", Some("de")) ) @@ -2094,10 +2085,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -2107,13 +2097,16 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { val newObjects = Seq( StringLiteralV2("The name of a Thing", Some("en")), - StringLiteralV2("Le nom d\\'une chose", Some("fr")), // This is SPARQL-escaped as it would be if taken from a JSON-LD request. + StringLiteralV2( + "Le nom d\\'une chose", + Some("fr") + ), // This is SPARQL-escaped as it would be if taken from a JSON-LD request. StringLiteralV2("Der Name eines Dinges", Some("de")) ) // Make an unescaped copy of the new comments, because this is how we will receive them in the API response. - val newObjectsUnescaped = newObjects.map { - case StringLiteralV2(text, lang) => StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) + val newObjectsUnescaped = newObjects.map { case StringLiteralV2(text, lang) => + StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) } responderManager ! ChangePropertyLabelsOrCommentsRequestV2( @@ -2126,19 +2119,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val readPropertyInfo = externalOntology.properties(propertyIri) - readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( - newObjectsUnescaped) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo = externalOntology.properties(propertyIri) + readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( + newObjectsUnescaped + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2147,13 +2141,16 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { val newObjects = Seq( StringLiteralV2("The name of a Thing", Some("en")), - StringLiteralV2("Le nom d\\'une chose", Some("fr")), // This is SPARQL-escaped as it would be if taken from a JSON-LD request. + StringLiteralV2( + "Le nom d\\'une chose", + Some("fr") + ), // This is SPARQL-escaped as it would be if taken from a JSON-LD request. StringLiteralV2("Der Name eines Dinges", Some("de")) ) // Make an unescaped copy of the new comments, because this is how we will receive them in the API response. - val newObjectsUnescaped = newObjects.map { - case StringLiteralV2(text, lang) => StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) + val newObjectsUnescaped = newObjects.map { case StringLiteralV2(text, lang) => + StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) } responderManager ! ChangePropertyLabelsOrCommentsRequestV2( @@ -2166,19 +2163,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val readPropertyInfo = externalOntology.properties(propertyIri) - readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( - newObjectsUnescaped) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo = externalOntology.properties(propertyIri) + readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( + newObjectsUnescaped + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2204,8 +2202,10 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ), directCardinalities = Map( AnythingOntologyIri.makeEntityIri("hasName") -> KnoraCardinalityInfo(Cardinality.MayHaveOne), - AnythingOntologyIri.makeEntityIri("hasInteger") -> KnoraCardinalityInfo(cardinality = Cardinality.MayHaveOne, - guiOrder = Some(20)) + AnythingOntologyIri.makeEntityIri("hasInteger") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(20) + ) ), subClassOf = Set(AnythingOntologyIri.makeEntityIri("Thing")), ontologySchema = ApiV2Complex @@ -2219,8 +2219,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -2248,7 +2248,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { directCardinalities = Map( AnythingOntologyIri.makeEntityIri("hasOtherThing") -> KnoraCardinalityInfo(Cardinality.MustHaveOne), AnythingOntologyIri.makeEntityIri("hasBlueThing") -> KnoraCardinalityInfo( - cardinality = Cardinality.MustHaveOne) + cardinality = Cardinality.MustHaveOne + ) ), subClassOf = Set(AnythingOntologyIri.makeEntityIri("Thing")), ontologySchema = ApiV2Complex @@ -2262,10 +2263,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -2304,10 +2304,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -2345,20 +2344,22 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - Set(AnythingOntologyIri.makeEntityIri("hasInterestingThing"), - AnythingOntologyIri.makeEntityIri("hasInterestingThingValue")) - .subsetOf(readClassInfo.allResourcePropertyCardinalities.keySet) should ===(true) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + Set( + AnythingOntologyIri.makeEntityIri("hasInterestingThing"), + AnythingOntologyIri.makeEntityIri("hasInterestingThingValue") + ) + .subsetOf(readClassInfo.allResourcePropertyCardinalities.keySet) should ===(true) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2380,7 +2381,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { assert(metadata.ontologyIri.toOntologySchema(ApiV2Complex) == AnythingOntologyIri) assert(metadata.label.contains(newLabel)) val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) assert(newAnythingLastModDate.isAfter(anythingLastModDate)) anythingLastModDate = newAnythingLastModDate } @@ -2396,14 +2398,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2428,8 +2430,10 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ), directCardinalities = Map( AnythingOntologyIri.makeEntityIri("hasName") -> KnoraCardinalityInfo(Cardinality.MayHaveOne), - AnythingOntologyIri.makeEntityIri("hasInteger") -> KnoraCardinalityInfo(cardinality = Cardinality.MayHaveOne, - guiOrder = Some(20)) + AnythingOntologyIri.makeEntityIri("hasInteger") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(20) + ) ), subClassOf = Set(AnythingOntologyIri.makeEntityIri("Thing")), ontologySchema = ApiV2Complex @@ -2477,22 +2481,22 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { "http://api.knora.org/ontology/knora-api/v2#Resource".toSmartIri ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.allBaseClasses should ===(expectedAllBaseClasses) - readClassInfo.entityInfoContent should ===(classInfoContent) - readClassInfo.inheritedCardinalities.keySet - .contains("http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri) should ===(false) - readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.allBaseClasses should ===(expectedAllBaseClasses) + readClassInfo.entityInfoContent should ===(classInfoContent) + readClassInfo.inheritedCardinalities.keySet + .contains("http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri) should ===(false) + readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2538,19 +2542,19 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { "http://api.knora.org/ontology/knora-api/v2#hasStandoffLinkToValue" ).map(_.toSmartIri) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent should ===(classInfoContent) - readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent should ===(classInfoContent) + readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2573,8 +2577,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -2597,19 +2601,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( - newObjects) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( + newObjects + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2631,19 +2636,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( - newObjects) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( + newObjects + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2666,8 +2672,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -2681,8 +2687,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) // Make an unescaped copy of the new comments, because this is how we will receive them in the API response. - val newObjectsUnescaped = newObjects.map { - case StringLiteralV2(text, lang) => StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) + val newObjectsUnescaped = newObjects.map { case StringLiteralV2(text, lang) => + StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) } responderManager ! ChangeClassLabelsOrCommentsRequestV2( @@ -2695,19 +2701,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( - newObjectsUnescaped) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( + newObjectsUnescaped + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2720,8 +2727,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) // Make an unescaped copy of the new comments, because this is how we will receive them in the API response. - val newObjectsUnescaped = newObjects.map { - case StringLiteralV2(text, lang) => StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) + val newObjectsUnescaped = newObjects.map { case StringLiteralV2(text, lang) => + StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) } responderManager ! ChangeClassLabelsOrCommentsRequestV2( @@ -2734,19 +2741,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( - newObjectsUnescaped) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( + newObjectsUnescaped + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -2785,10 +2793,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -2827,10 +2834,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -2869,10 +2875,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -2911,10 +2916,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -2955,10 +2959,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -2999,10 +3002,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -3043,20 +3045,21 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent should ===(classInfoContent) - readClassInfo.allCardinalities(AnythingOntologyIri.makeEntityIri("hasBoolean")).cardinality should ===( - Cardinality.MustHaveOne) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent should ===(classInfoContent) + readClassInfo.allCardinalities(AnythingOntologyIri.makeEntityIri("hasBoolean")).cardinality should ===( + Cardinality.MustHaveOne + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3097,10 +3100,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -3116,10 +3118,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -3136,14 +3137,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Check that both properties were deleted. @@ -3164,18 +3165,16 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! linkPropGetRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[NotFoundException] should ===(true) } responderManager ! linkValuePropGetRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[NotFoundException] should ===(true) } // Reload the ontology cache and see if we get the same result. @@ -3189,18 +3188,16 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! linkPropGetRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[NotFoundException] should ===(true) } responderManager ! linkValuePropGetRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -3254,18 +3251,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val property = externalOntology.properties(propertyIri) - property.entityInfoContent should ===(propertyInfoContent) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val property = externalOntology.properties(propertyIri) + property.entityInfoContent should ===(propertyInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3282,31 +3279,35 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val property = externalOntology.properties(propertyIri) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val property = externalOntology.properties(propertyIri) - 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( + 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( - OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri) should ===( - PredicateInfoV2( - predicateIri = OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri, - objects = Seq(StringLiteralV2("size=80")) - )) + property.entityInfoContent.predicates( + OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri + ) should ===( + PredicateInfoV2( + predicateIri = OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri, + objects = Seq(StringLiteralV2("size=80")) + ) + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3323,23 +3324,23 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val property = externalOntology.properties(propertyIri) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val property = externalOntology.properties(propertyIri) - property.entityInfoContent.predicates - .get(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri) should ===(None) + property.entityInfoContent.predicates + .get(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri) should ===(None) - property.entityInfoContent.predicates - .get(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri) should ===(None) + property.entityInfoContent.predicates + .get(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri) should ===(None) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3386,10 +3387,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -3428,10 +3428,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -3466,18 +3465,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent should ===(classInfoContent) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent should ===(classInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3506,10 +3505,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -3524,8 +3522,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -3540,14 +3538,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3564,9 +3562,10 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) ), directCardinalities = Map( - AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(0)) + AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ) ), ontologySchema = ApiV2Complex ) @@ -3579,14 +3578,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } "create a link property, anything:hasOtherNothing, and add a cardinality for it to the class anything:Nothing" in { - val classIri = AnythingOntologyIri.makeEntityIri("Nothing") + val classIri = AnythingOntologyIri.makeEntityIri("Nothing") val propertyIri = AnythingOntologyIri.makeEntityIri("hasOtherNothing") val propertyInfoContent = PropertyInfoContentV2( @@ -3629,14 +3628,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } val classInfoContent = ClassInfoContentV2( @@ -3663,8 +3662,10 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { val expectedDirectCardinalities = Map( propertyIri -> KnoraCardinalityInfo(cardinality = Cardinality.MayHaveOne, guiOrder = Some(0)), - propertyIri.fromLinkPropToLinkValueProp -> KnoraCardinalityInfo(cardinality = Cardinality.MayHaveOne, - guiOrder = Some(0)) + propertyIri.fromLinkPropToLinkValueProp -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ) ) val expectedProperties = Set( @@ -3679,20 +3680,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { "http://api.knora.org/ontology/knora-api/v2#Resource".toSmartIri ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - assert(readClassInfo.allBaseClasses == expectedAllBaseClasses) - readClassInfo.entityInfoContent.directCardinalities should ===(expectedDirectCardinalities) - readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + assert(readClassInfo.allBaseClasses == expectedAllBaseClasses) + readClassInfo.entityInfoContent.directCardinalities should ===(expectedDirectCardinalities) + readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3708,9 +3709,10 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) ), directCardinalities = Map( - AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveMany, - guiOrder = Some(0)) + AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveMany, + guiOrder = Some(0) + ) ), ontologySchema = ApiV2Complex ) @@ -3723,9 +3725,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -3741,9 +3742,10 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) ), directCardinalities = Map( - AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(0)) + AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ) ), ontologySchema = ApiV2Complex ) @@ -3757,15 +3759,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) val expectedDirectCardinalities = Map( - AnythingOntologyIri.makeEntityIri("hasOtherNothing") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(0)), - AnythingOntologyIri.makeEntityIri("hasOtherNothingValue") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(0)), + AnythingOntologyIri.makeEntityIri("hasOtherNothing") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ), + AnythingOntologyIri.makeEntityIri("hasOtherNothingValue") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ), AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo( cardinality = Cardinality.MayHaveOne, - guiOrder = Some(0)) + guiOrder = Some(0) + ) ) val expectedProperties = Set( @@ -3776,19 +3781,19 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { AnythingOntologyIri.makeEntityIri("hasNothingness") ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent.directCardinalities should ===(expectedDirectCardinalities) - readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.directCardinalities should ===(expectedDirectCardinalities) + readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3817,10 +3822,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -3849,16 +3853,16 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3907,18 +3911,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val property = externalOntology.properties(propertyIri) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val property = externalOntology.properties(propertyIri) - property.entityInfoContent should ===(propertyInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + property.entityInfoContent should ===(propertyInfoContent) + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -3933,12 +3937,12 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { objects = Seq(SmartIriLiteralV2(OntologyConstants.Owl.Class.toSmartIri)) ) ), - directCardinalities = - Map( - AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(1)) - ), + directCardinalities = Map( + AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(1) + ) + ), ontologySchema = ApiV2Complex ) @@ -3951,17 +3955,22 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) val expectedDirectCardinalities = Map( - AnythingOntologyIri.makeEntityIri("hasOtherNothing") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(0)), - AnythingOntologyIri.makeEntityIri("hasOtherNothingValue") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(0)), + AnythingOntologyIri.makeEntityIri("hasOtherNothing") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ), + AnythingOntologyIri.makeEntityIri("hasOtherNothingValue") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ), AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo( cardinality = Cardinality.MayHaveOne, - guiOrder = Some(0)), - AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo(cardinality = Cardinality.MayHaveOne, - guiOrder = Some(1)) + guiOrder = Some(0) + ), + AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(1) + ) ) val expectedProperties = Set( @@ -3973,19 +3982,19 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { AnythingOntologyIri.makeEntityIri("hasEmptiness") ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent.directCardinalities should ===(expectedDirectCardinalities) - readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.directCardinalities should ===(expectedDirectCardinalities) + readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4001,12 +4010,12 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { objects = Seq(SmartIriLiteralV2(OntologyConstants.Owl.Class.toSmartIri)) ) ), - directCardinalities = - Map( - AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(0)) - ), + directCardinalities = Map( + AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ) + ), ontologySchema = ApiV2Complex ) @@ -4018,8 +4027,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -4036,15 +4045,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) ), directCardinalities = Map( - AnythingOntologyIri.makeEntityIri("hasOtherNothing") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(1)), - AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(2)), + AnythingOntologyIri.makeEntityIri("hasOtherNothing") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(1) + ), + AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(2) + ), AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo( cardinality = Cardinality.MayHaveOne, - guiOrder = Some(3)) + guiOrder = Some(3) + ) ), ontologySchema = ApiV2Complex ) @@ -4058,31 +4070,36 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { ) val expectedCardinalities = Map( - AnythingOntologyIri.makeEntityIri("hasOtherNothing") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(1)), - AnythingOntologyIri.makeEntityIri("hasOtherNothingValue") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(1)), + AnythingOntologyIri.makeEntityIri("hasOtherNothing") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(1) + ), + AnythingOntologyIri.makeEntityIri("hasOtherNothingValue") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(1) + ), AnythingOntologyIri.makeEntityIri("hasNothingness") -> KnoraCardinalityInfo( cardinality = Cardinality.MayHaveOne, - guiOrder = Some(2)), - AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo(cardinality = Cardinality.MayHaveOne, - guiOrder = Some(3)) + guiOrder = Some(2) + ), + AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(3) + ) ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent.directCardinalities should ===(expectedCardinalities) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.directCardinalities should ===(expectedCardinalities) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4097,12 +4114,12 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { objects = Seq(SmartIriLiteralV2(OntologyConstants.Owl.Class.toSmartIri)) ) ), - directCardinalities = - Map( - AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo(cardinality = - Cardinality.MayHaveOne, - guiOrder = Some(0)) - ), + directCardinalities = Map( + AnythingOntologyIri.makeEntityIri("hasEmptiness") -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne, + guiOrder = Some(0) + ) + ), ontologySchema = ApiV2Complex ) @@ -4125,20 +4142,20 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { "http://api.knora.org/ontology/knora-api/v2#Resource".toSmartIri ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - assert(readClassInfo.allBaseClasses == expectedAllBaseClasses) - readClassInfo.entityInfoContent.directCardinalities should ===(classInfoContent.directCardinalities) - readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + assert(readClassInfo.allBaseClasses == expectedAllBaseClasses) + readClassInfo.entityInfoContent.directCardinalities should ===(classInfoContent.directCardinalities) + readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4153,10 +4170,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -4171,14 +4187,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4193,10 +4209,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -4223,10 +4238,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -4257,19 +4271,19 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { OntologyConstants.KnoraApiV2Complex.HasStandoffLinkToValue.toSmartIri ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent.directCardinalities should ===(classInfoContent.directCardinalities) - readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.directCardinalities should ===(classInfoContent.directCardinalities) + readClassInfo.allResourcePropertyCardinalities.keySet should ===(expectedProperties) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4284,10 +4298,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[EditConflictException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[EditConflictException] should ===(true) } } @@ -4302,10 +4315,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingNonAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -4320,14 +4332,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } val hasEmptiness = AnythingOntologyIri.makeEntityIri("hasEmptiness") @@ -4340,14 +4352,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4362,14 +4374,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4404,10 +4416,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -4444,10 +4455,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -4491,10 +4501,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -4542,10 +4551,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -4589,10 +4597,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - if (printErrorMessages) println(msg.cause.getMessage) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + if (printErrorMessages) println(msg.cause.getMessage) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -4627,18 +4634,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent should ===(classInfoContent) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent should ===(classInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4653,14 +4660,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4697,18 +4704,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - readClassInfo.entityInfoContent should ===(classInfoContent) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent should ===(classInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4723,14 +4730,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4769,18 +4776,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val property = externalOntology.properties(propertyIri) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val property = externalOntology.properties(propertyIri) - property.entityInfoContent should ===(propertyInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + property.entityInfoContent should ===(propertyInfoContent) + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4795,14 +4802,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4845,18 +4852,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val property = externalOntology.properties(propertyIri) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val property = externalOntology.properties(propertyIri) - property.entityInfoContent should ===(propertyInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + property.entityInfoContent should ===(propertyInfoContent) + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4871,14 +4878,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4917,18 +4924,18 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.properties.size == 1) - val property = externalOntology.properties(propertyIri) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val property = externalOntology.properties(propertyIri) - property.entityInfoContent should ===(propertyInfoContent) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + property.entityInfoContent should ===(propertyInfoContent) + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4943,14 +4950,14 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyMetadataV2 => - assert(msg.ontologies.size == 1) - val metadata = msg.ontologies.head - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyMetadataV2 => + assert(msg.ontologies.size == 1) + val metadata = msg.ontologies.head + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } } @@ -4966,7 +4973,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { StringLiteralV2( value = "test class", language = Some("en") - )) + ) + ) ), "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri -> PredicateInfoV2( predicateIri = "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri, @@ -4974,7 +4982,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { StringLiteralV2( value = "A test class", language = Some("en") - )) + ) + ) ), "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, @@ -4991,12 +5000,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate - .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate + .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Create a text property. @@ -5011,7 +5019,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { StringLiteralV2( value = "test text property", language = Some("en") - )) + ) + ) ), "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri -> PredicateInfoV2( predicateIri = "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri, @@ -5024,7 +5033,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { StringLiteralV2( value = "A test text property", language = Some("en") - )) + ) + ) ), "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri -> PredicateInfoV2( predicateIri = "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri, @@ -5045,12 +5055,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate - .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate + .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Create an integer property. @@ -5065,7 +5074,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { StringLiteralV2( value = "test int property", language = Some("en") - )) + ) + ) ), "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri -> PredicateInfoV2( predicateIri = "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri, @@ -5078,7 +5088,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { StringLiteralV2( value = "A test int property", language = Some("en") - )) + ) + ) ), "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri -> PredicateInfoV2( predicateIri = "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri, @@ -5099,12 +5110,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate - .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate + .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Create a link property. @@ -5119,7 +5129,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { StringLiteralV2( value = "test link property", language = Some("en") - )) + ) + ) ), "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri -> PredicateInfoV2( predicateIri = "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri, @@ -5132,7 +5143,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { StringLiteralV2( value = "A test link property", language = Some("en") - )) + ) + ) ), "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri -> PredicateInfoV2( predicateIri = "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri, @@ -5153,12 +5165,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate - .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate + .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Add cardinalities to the class. @@ -5169,7 +5180,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { "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/anything/v2#TestClass".toSmartIri, ontologySchema = ApiV2Complex, directCardinalities = Map( @@ -5182,7 +5194,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { "http://0.0.0.0:3333/ontology/0001/anything/v2#testLinkProp".toSmartIri -> KnoraCardinalityInfo( cardinality = Cardinality.MayHaveOne ) - ), + ) ), lastModificationDate = anythingLastModDate, apiRequestID = UUID.randomUUID, @@ -5190,12 +5202,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate - .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate + .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Remove the link value cardinality from the class. @@ -5206,7 +5217,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { "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/anything/v2#TestClass".toSmartIri, ontologySchema = ApiV2Complex, directCardinalities = Map( @@ -5216,7 +5228,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { "http://0.0.0.0:3333/ontology/0001/anything/v2#testIntProp".toSmartIri -> KnoraCardinalityInfo( cardinality = Cardinality.MayHaveOne ) - ), + ) ), lastModificationDate = anythingLastModDate, apiRequestID = UUID.randomUUID, @@ -5224,12 +5236,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate - .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val newAnythingLastModDate = msg.ontologyMetadata.lastModificationDate + .getOrElse(throw AssertionException(s"${msg.ontologyMetadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } // Check that the correct blank nodes were stored for the cardinalities. @@ -5243,14 +5254,325 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { | rdfs:subClassOf ?restriction . | FILTER isBlank(?restriction) | ?restriction owl:onProperty ?cardinalityProp . - |}""".stripMargin) + |}""".stripMargin + ) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - assert( - msg.results.bindings.map(_.rowMap("cardinalityProp")).sorted == Seq( - "http://www.knora.org/ontology/0001/anything#testIntProp", - "http://www.knora.org/ontology/0001/anything#testTextProp")) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + assert( + msg.results.bindings.map(_.rowMap("cardinalityProp")).sorted == Seq( + "http://www.knora.org/ontology/0001/anything#testIntProp", + "http://www.knora.org/ontology/0001/anything#testTextProp" + ) + ) + } + } + + "create a class with two cardinalities, use one in data, and allow only removal of the cardinality for the property not used in data" in { + + // Create a class with no cardinalities. + + responderManager ! CreateClassRequestV2( + classInfoContent = ClassInfoContentV2( + predicates = Map( + "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri -> PredicateInfoV2( + predicateIri = "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri, + objects = Vector( + StringLiteralV2( + value = "A Blue Free Test class", + language = Some("en") + ) + ) + ), + "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri -> PredicateInfoV2( + predicateIri = "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri, + objects = Vector( + StringLiteralV2( + value = "A Blue Free Test class used for testing cardinalities", + language = Some("en") + ) + ) + ), + "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#BlueFreeTestClass".toSmartIri, + ontologySchema = ApiV2Complex, + subClassOf = Set("http://api.knora.org/ontology/knora-api/v2#Resource".toSmartIri) + ), + lastModificationDate = freetestLastModData, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + 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 + } + + // Create a text property. + + responderManager ! CreatePropertyRequestV2( + propertyInfoContent = PropertyInfoContentV2( + propertyIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2#hasBlueTestTextProp".toSmartIri, + predicates = Map( + "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri -> PredicateInfoV2( + predicateIri = "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri, + objects = Vector( + StringLiteralV2( + value = "blue test text property", + language = Some("en") + ) + ) + ), + "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri -> PredicateInfoV2( + predicateIri = "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri, + objects = Vector( + SmartIriLiteralV2(value = "http://0.0.0.0:3333/ontology/0001/freetest/v2#BlueFreeTestClass".toSmartIri) + ) + ), + "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri -> PredicateInfoV2( + predicateIri = "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri, + objects = Vector( + StringLiteralV2( + value = "A blue test text property", + language = Some("en") + ) + ) + ), + "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri -> PredicateInfoV2( + predicateIri = "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri, + objects = + Vector(SmartIriLiteralV2(value = "http://api.knora.org/ontology/knora-api/v2#TextValue".toSmartIri)) + ), + "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#ObjectProperty".toSmartIri)) + ) + ), + subPropertyOf = Set("http://api.knora.org/ontology/knora-api/v2#hasValue".toSmartIri), + ontologySchema = ApiV2Complex + ), + lastModificationDate = freetestLastModData, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + 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 + } + + // Create an integer property. + + responderManager ! CreatePropertyRequestV2( + propertyInfoContent = PropertyInfoContentV2( + propertyIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2#hasBlueTestIntProp".toSmartIri, + predicates = Map( + "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri -> PredicateInfoV2( + predicateIri = "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri, + objects = Vector( + StringLiteralV2( + value = "blue test integer property", + language = Some("en") + ) + ) + ), + "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri -> PredicateInfoV2( + predicateIri = "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri, + objects = Vector( + SmartIriLiteralV2(value = "http://0.0.0.0:3333/ontology/0001/freetest/v2#BlueFreeTestClass".toSmartIri) + ) + ), + "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri -> PredicateInfoV2( + predicateIri = "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri, + objects = Vector( + StringLiteralV2( + value = "A blue test integer property", + language = Some("en") + ) + ) + ), + "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri -> PredicateInfoV2( + predicateIri = "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri, + objects = + Vector(SmartIriLiteralV2(value = "http://api.knora.org/ontology/knora-api/v2#IntValue".toSmartIri)) + ), + "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#ObjectProperty".toSmartIri)) + ) + ), + subPropertyOf = Set("http://api.knora.org/ontology/knora-api/v2#hasValue".toSmartIri), + ontologySchema = ApiV2Complex + ), + lastModificationDate = freetestLastModData, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + 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 + } + + // Add cardinalities to the class. + + 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#BlueFreeTestClass".toSmartIri, + ontologySchema = ApiV2Complex, + directCardinalities = Map( + "http://0.0.0.0:3333/ontology/0001/freetest/v2#hasBlueTestTextProp".toSmartIri -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne + ), + "http://0.0.0.0:3333/ontology/0001/freetest/v2#hasBlueTestIntProp".toSmartIri -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne + ) + ) + ), + lastModificationDate = freetestLastModData, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + 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 + } + + // Create a resource of #BlueTestClass using only #hasBlueTestIntProp. + + val resourceIri: IRI = stringFormatter.makeRandomResourceIri(SharedTestDataADM.anythingProject.shortcode) + + val inputValues: Map[SmartIri, Seq[CreateValueInNewResourceV2]] = Map( + "http://0.0.0.0:3333/ontology/0001/freetest/v2#hasBlueTestIntProp".toSmartIri -> Seq( + CreateValueInNewResourceV2( + valueContent = IntegerValueContentV2( + ontologySchema = ApiV2Complex, + valueHasInteger = 5, + comment = Some("this is the number five") + ), + permissions = Some("CR knora-admin:Creator|V http://rdfh.ch/groups/0001/thing-searcher") + ) + ) + ) + + val inputResource = CreateResourceV2( + resourceIri = Some(resourceIri.toSmartIri), + resourceClassIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2#BlueFreeTestClass".toSmartIri, + label = "my blue test class thing instance", + values = inputValues, + projectADM = SharedTestDataADM.anythingProject + ) + + responderManager ! CreateResourceRequestV2( + createResource = inputResource, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser, + apiRequestID = UUID.randomUUID + ) + + expectMsgType[ReadResourcesSequenceV2](timeout) + + // Successfully check if the cardinality can be deleted + + responderManager ! CanDeleteCardinalitiesFromClassRequestV2( + 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#BlueFreeTestClass".toSmartIri, + ontologySchema = ApiV2Complex, + directCardinalities = Map( + "http://0.0.0.0:3333/ontology/0001/freetest/v2#hasBlueTestTextProp".toSmartIri -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne + ) + ) + ), + lastModificationDate = freetestLastModData, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { case msg: CanDoResponseV2 => + assert(msg.canDo) + } + + // Successfully remove the (unused) text value cardinality from the class. + + responderManager ! DeleteCardinalitiesFromClassRequestV2( + 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#BlueFreeTestClass".toSmartIri, + ontologySchema = ApiV2Complex, + directCardinalities = Map( + "http://0.0.0.0:3333/ontology/0001/freetest/v2#hasBlueTestTextProp".toSmartIri -> KnoraCardinalityInfo( + cardinality = Cardinality.MayHaveOne + ) + ) + ), + lastModificationDate = freetestLastModData, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + 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 + } + + // Check that the correct blank nodes were stored for the cardinalities. + + storeManager ! SparqlSelectRequest( + """PREFIX rdfs: + |PREFIX owl: + | + |SELECT ?cardinalityProp + |WHERE { + | rdfs:subClassOf ?restriction . + | FILTER isBlank(?restriction) + | ?restriction owl:onProperty ?cardinalityProp . + |}""".stripMargin + ) + + expectMsgPF(timeout) { case msg: SparqlSelectResult => + assert( + msg.results.bindings.map(_.rowMap("cardinalityProp")).sorted == Seq( + "http://www.knora.org/ontology/0001/freetest#hasBlueTestIntProp" + ) + ) } } @@ -5281,20 +5603,21 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(classIri) - assert( - readClassInfo.entityInfoContent - .directCardinalities(AnythingOntologyIri.makeEntityIri("hasText")) == newCardinality) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + assert( + readClassInfo.entityInfoContent + .directCardinalities(AnythingOntologyIri.makeEntityIri("hasText")) == newCardinality + ) - val metadata = externalOntology.ontologyMetadata - val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( - throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) - assert(newAnythingLastModDate.isAfter(anythingLastModDate)) - anythingLastModDate = newAnythingLastModDate + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date") + ) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate } responderManager ! ClassesGetRequestV2( @@ -5303,12 +5626,11 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingAdminUser ) - expectMsgPF(timeout) { - case msg: ReadOntologyV2 => - val externalOntology = msg.toOntologySchema(ApiV2Complex) - assert(externalOntology.classes.size == 1) - val readClassInfo = externalOntology.classes(AnythingOntologyIri.makeEntityIri("ThingWithSeqnum")) - assert(readClassInfo.inheritedCardinalities(AnythingOntologyIri.makeEntityIri("hasText")) == newCardinality) + expectMsgPF(timeout) { case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(AnythingOntologyIri.makeEntityIri("ThingWithSeqnum")) + assert(readClassInfo.inheritedCardinalities(AnythingOntologyIri.makeEntityIri("hasText")) == newCardinality) } } @@ -5317,7 +5639,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/onto-without-project.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5327,7 +5650,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/missing-link-value-cardinality-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5337,7 +5661,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/missing-link-cardinality-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5347,7 +5672,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-incompatible-with-scc-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5357,7 +5683,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-without-label-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5367,7 +5694,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/property-without-label-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5377,7 +5705,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/resource-class-is-standoff-class-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5387,7 +5716,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-missing-property-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5397,7 +5727,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-non-resource-prop-cardinality-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5407,7 +5738,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-cardinality-on-kbresprop-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5417,7 +5749,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-cardinality-on-kbhasvalue-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5427,7 +5760,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/resource-class-with-invalid-base-class-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5437,7 +5771,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/standoff-class-with-resprop-cardinality-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5447,7 +5782,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/standoff-class-with-invalid-base-class-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5457,7 +5793,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-non-knora-scc-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5467,7 +5804,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-value-scc-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5477,7 +5815,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-guielement-scc-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5487,7 +5826,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-non-knora-occ-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5497,7 +5837,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-incompatible-occ-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5507,7 +5848,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-misdefined-link-property-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5517,7 +5859,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-misdefined-link-value-property-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5527,7 +5870,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/resource-prop-without-occ-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5537,7 +5881,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/resource-prop-without-label-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5547,7 +5892,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-both-value-and-link-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5557,7 +5903,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/filevalue-prop-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5567,7 +5914,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/resource-prop-wrong-base-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5577,7 +5925,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-guiorder-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5587,7 +5936,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/cardinality-with-guielement-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5597,7 +5947,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/cardinality-with-guiattribute-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5607,7 +5958,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/transitive-prop.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5617,7 +5969,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-inherits-prop-and-subprop-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5627,7 +5980,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-mismatched-link-cardinalities-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5637,7 +5991,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/invalid-card-on-boolean-prop.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5647,7 +6002,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-non-shared-cardinality.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5657,7 +6013,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/class-with-non-shared-base-class.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5667,7 +6024,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-non-shared-base-prop.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5677,7 +6035,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-non-shared-scc.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5687,7 +6046,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/prop-with-non-shared-occ.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } @@ -5697,7 +6057,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { RdfDataObject( path = "test_data/responders.v2.OntologyResponderV2Spec/conflicting-cardinalities-onto.ttl", name = "http://www.knora.org/ontology/invalid" - )) + ) + ) loadInvalidTestData(invalidOnto) } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/BUILD.bazel b/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/BUILD.bazel new file mode 100644 index 0000000000..4a92416543 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/BUILD.bazel @@ -0,0 +1,42 @@ +package(default_visibility = ["//visibility:public"]) + +load("@io_bazel_rules_scala//scala:scala.bzl", "scala_test") +load("//third_party:dependencies.bzl", "ALL_WEBAPI_MAIN_DEPENDENCIES", "BASE_TEST_DEPENDENCIES", "BASE_TEST_DEPENDENCIES_WITH_JSON", "BASE_TEST_DEPENDENCIES_WITH_JSON_LD") + +scala_test( + name = "DeleteCardinalitiesFromClassSpec", + size = "small", # 60s + srcs = [ + "DeleteCardinalitiesFromClassSpec.scala", + ], + data = [ + "//knora-ontologies", + "//test_data", + ], + jvm_flags = ["-Dconfig.resource=fuseki.conf"], + resources = [ + "//webapi/scripts:fuseki_repository_config_ttl_template", + "//webapi/src/main/resources", + "//webapi/src/test/resources", + ], + scalacopts = ["-deprecation"], + unused_dependency_checker_mode = "warn", + deps = [ + "//webapi:test_library", + "//webapi/src/main/scala/org/knora/webapi", + "//webapi/src/main/scala/org/knora/webapi/instrumentation", + "//webapi/src/main/scala/org/knora/webapi/messages", + "//webapi/src/main/scala/org/knora/webapi/responders/v2/ontology", + "//webapi/src/main/scala/org/knora/webapi/settings", + "//webapi/src/main/scala/org/knora/webapi/store", + "@maven//:com_typesafe_akka_akka_actor_2_13", + "@maven//:com_typesafe_config", + "@maven//:com_typesafe_scala_logging_scala_logging_2_13", + "@maven//:org_scalactic_scalactic_2_13", + "@maven//:org_scalatest_scalatest_compatible", + "@maven//:org_scalatest_scalatest_core_2_13", + "@maven//:org_scalatest_scalatest_matchers_core_2_13", + "@maven//:org_scalatest_scalatest_shouldmatchers_2_13", + "@maven//:org_scalatest_scalatest_wordspec_2_13", + ], +) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/DeleteCardinalitiesFromClassSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/DeleteCardinalitiesFromClassSpec.scala new file mode 100644 index 0000000000..a1ce4d1eee --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/DeleteCardinalitiesFromClassSpec.scala @@ -0,0 +1,88 @@ +/* + * Copyright © 2015-2021 the contributors (see Contributors.md). + * + * This file is part of the DaSCH Service Platform. + * + * The DaSCH Service Platform 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. + * + * The DaSCH Service Platform 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 the DaSCH Service Platform. If not, see + * . + */ + +package org.knora.webapi.responders.v2.ontology + +import akka.actor.Props +import org.knora.webapi.messages.IriConversions._ +import org.knora.webapi.messages.StringFormatter +import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject +import org.knora.webapi.settings.KnoraDispatchers +import org.knora.webapi.store.triplestore.http.HttpTriplestoreConnector +import org.knora.webapi.{IntegrationSpec, InternalSchema, TestContainerFuseki} + +/** + * This spec is used to test [[org.knora.webapi.responders.v2.ontology.Cardinalities]]. + * Adding the [[TestContainerFuseki.PortConfig]] config will start the Fuseki container and make it + * available to the test. + */ +class DeleteCardinalitiesFromClassSpec extends IntegrationSpec(TestContainerFuseki.PortConfig) { + + private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance + + val additionalTestData = List( + RdfDataObject( + path = "test_data/ontologies/freetest-onto.ttl", + name = "http://www.knora.org/ontology/0001/freetest" + ), + RdfDataObject(path = "test_data/all_data/freetest-data.ttl", name = "http://www.knora.org/data/0001/freetest") + ) + + // start fuseki http connector actor + private val fusekiActor = system.actorOf( + Props(new HttpTriplestoreConnector()).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), + name = "httpTriplestoreConnector" + ) + + override def beforeAll(): Unit = { + waitForReadyTriplestore(fusekiActor) + loadTestData(fusekiActor, additionalTestData) + } + + // use started actor in tests instead of the store manager + "DeleteCardinalitiesFromClass" should { + "detect that property is in use, when used in a resource" in { + val FreetestOntologyIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2".toSmartIri + val internalPropertyIri = FreetestOntologyIri.makeEntityIri("hasText").toOntologySchema(InternalSchema) + println(s"internalPropertyIri: $internalPropertyIri") + + val resF = Cardinalities.isPropertyUsedInResources(settings, fusekiActor, internalPropertyIri) + resF map { res => println(res); assert(res, "property is used in resource (instance of resource class)") } + } + + "detect that property is in use, when used in a resource of a subclass" in { + val FreetestOntologyIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2".toSmartIri + val internalPropertyIri = FreetestOntologyIri.makeEntityIri("hasDecimal").toOntologySchema(InternalSchema) + println(s"internalPropertyIri: $internalPropertyIri") + + val resF = Cardinalities.isPropertyUsedInResources(settings, fusekiActor, internalPropertyIri) + resF map { res => println(res); assert(res, "property is used in a resource of subclass") } + } + + "detect that property is not in use, when not used in any resource" in { + val FreetestOntologyIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2".toSmartIri + val internalPropertyIri = FreetestOntologyIri.makeEntityIri("hasInteger").toOntologySchema(InternalSchema) + println(s"internalPropertyIri: $internalPropertyIri") + + val resF = Cardinalities.isPropertyUsedInResources(settings, fusekiActor, internalPropertyIri) + resF map { res => println(res); assert(!res, "property is not used") } + } + } +} diff --git a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala index 7382b2a49f..b2db5b1922 100644 --- a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala +++ b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala @@ -26,56 +26,60 @@ object SharedOntologyTestDataADM { val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" // anything - val ANYTHING_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/anything" - val ANYTHING_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/anything/v2" - val ANYTHING_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0001/anything" + val ANYTHING_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/anything" + val ANYTHING_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/anything/v2" + val ANYTHING_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0001/anything" val ANYTHING_THING_RESOURCE_CLASS_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "Thing" val ANYTHING_HasListItem_PROPERTY_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "hasListItem" - val ANYTHING_HasDate_PROPERTY_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "hasDate" + val ANYTHING_HasDate_PROPERTY_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "hasDate" + + // freetest + val FREETEST_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/freetest" + val FREETEST_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/freetest/v2" // minimal - val MINIMAL_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/minimal" + val MINIMAL_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/minimal" val MINIMAL_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/minimal/v2" // images - val IMAGES_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/00FF/images" - val IMAGES_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/00FF/images/v2" - val IMAGES_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/00FF/images" - val IMAGES_TITEL_PROPERTY: IRI = IMAGES_ONTOLOGY_IRI + "#" + "titel" - val IMAGES_TITEL_PROPERTY_LocalHost: IRI = IMAGES_ONTOLOGY_IRI_LocalHost + "#" + "titel" - val IMAGES_BILD_RESOURCE_CLASS: IRI = IMAGES_ONTOLOGY_IRI + "#" + "bild" + val IMAGES_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/00FF/images" + val IMAGES_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/00FF/images/v2" + val IMAGES_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/00FF/images" + val IMAGES_TITEL_PROPERTY: IRI = IMAGES_ONTOLOGY_IRI + "#" + "titel" + val IMAGES_TITEL_PROPERTY_LocalHost: IRI = IMAGES_ONTOLOGY_IRI_LocalHost + "#" + "titel" + val IMAGES_BILD_RESOURCE_CLASS: IRI = IMAGES_ONTOLOGY_IRI + "#" + "bild" val IMAGES_BILD_RESOURCE_CLASS_LocalHost: IRI = IMAGES_ONTOLOGY_IRI_LocalHost + "#" + "bild" // beol - val BEOL_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0801/beol" + val BEOL_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0801/beol" val BEOL_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0801/beol/v2" - val BEOL_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0801/beol" + val BEOL_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0801/beol" // biblio - val BIBLIO_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0801/biblio" + val BIBLIO_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0801/biblio" val BIBLIO_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0801/biblio/v2" - val BIBLIO_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0801/biblio" + val BIBLIO_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0801/biblio" // incunabula - val INCUNABULA_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0803/incunabula" - val INCUNABULA_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0803/incunabula/v2" - val INCUNABULA_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0803/incunabula" - val INCUNABULA_BOOK_RESOURCE_CLASS: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "book" + val INCUNABULA_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0803/incunabula" + val INCUNABULA_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0803/incunabula/v2" + val INCUNABULA_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0803/incunabula" + val INCUNABULA_BOOK_RESOURCE_CLASS: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "book" val INCUNABULA_BOOK_RESOURCE_CLASS_LocalHost: IRI = INCUNABULA_ONTOLOGY_IRI_LocalHost + "#" + "book" - val INCUNABULA_PAGE_RESOURCE_CLASS: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "page" + val INCUNABULA_PAGE_RESOURCE_CLASS: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "page" val INCUNABULA_PAGE_RESOURCE_CLASS_LocalHost: IRI = INCUNABULA_ONTOLOGY_IRI_LocalHost + "#" + "page" - val INCUNABULA_PartOf_Property: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "partOfValue" - val INCUNABULA_PartOf_Property_LocalHost: IRI = INCUNABULA_ONTOLOGY_IRI_LocalHost + "#" + "partOfValue" + val INCUNABULA_PartOf_Property: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "partOfValue" + val INCUNABULA_PartOf_Property_LocalHost: IRI = INCUNABULA_ONTOLOGY_IRI_LocalHost + "#" + "partOfValue" // dokubib - val DOKUBIB_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0804/dokubib" + val DOKUBIB_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0804/dokubib" val DOKUBIB_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0804/dokubib/v2" - val DOKUBIB_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0804/dokubib" + val DOKUBIB_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0804/dokubib" // webern - val WEBERN_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0806/webern" + val WEBERN_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0806/webern" val WEBERN_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0806/webern/v2" - val WEBERN_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0806/webern" + val WEBERN_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0806/webern" //foo ontology val FOO_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/00FF/foo/v2" diff --git a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImplSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImplSpec.scala index 276bf8f6d2..22d68c7195 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImplSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImplSpec.scala @@ -25,66 +25,67 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.{ProjectADM, P import org.knora.webapi.messages.admin.responder.usersmessages.{UserADM, UserIdentifierADM} import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.store.cacheservice.settings.CacheServiceSettings -import org.knora.webapi.{RedisTestContainer, UnitSpec} +import org.knora.webapi.{TestContainerRedis, UnitSpec} import scala.concurrent.ExecutionContext /** - * This spec is used to test [[org.knora.webapi.store.cacheservice.redis.CacheServiceRedisImplSpec]]. - * Adding the [[RedisTestContainer.PortConfig]] config will start the Redis container and make it - * available to the test. - */ -class CacheServiceRedisImplSpec extends UnitSpec(RedisTestContainer.PortConfig) { + * This spec is used to test [[org.knora.webapi.store.cacheservice.redis.CacheServiceRedisImpl]]. + * Adding the [[TestContainerRedis.PortConfig]] config will start the Redis container and make it + * available to the test. + */ +class CacheServiceRedisImplSpec extends UnitSpec(TestContainerRedis.PortConfig) { implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - implicit val ec: ExecutionContext = ExecutionContext.global + implicit val ec: ExecutionContext = ExecutionContext.global - private val user: UserADM = SharedTestDataADM.imagesUser01 + private val user: UserADM = SharedTestDataADM.imagesUser01 private val project: ProjectADM = SharedTestDataADM.imagesProject private val redisCache: CacheServiceRedisImpl = new CacheServiceRedisImpl( - new CacheServiceSettings(RedisTestContainer.PortConfig)) + new CacheServiceSettings(TestContainerRedis.PortConfig) + ) "The CacheServiceRedisImpl" should { "successfully store a user" in { val resFuture = redisCache.putUserADM(user) - resFuture map {res => res should equal(true)} + resFuture map { res => res should equal(true) } } "successfully retrieve a user by IRI" in { val resFuture = redisCache.getUserADM(UserIdentifierADM(maybeIri = Some(user.id))) - resFuture map {res => res should equal(Some(user))} + resFuture map { res => res should equal(Some(user)) } } "successfully retrieve a user by USERNAME" in { val resFuture = redisCache.getUserADM(UserIdentifierADM(maybeUsername = Some(user.username))) - resFuture map {res => res should equal(Some(user))} + resFuture map { res => res should equal(Some(user)) } } "successfully retrieve a user by EMAIL" in { val resFuture = redisCache.getUserADM(UserIdentifierADM(maybeEmail = Some(user.email))) - resFuture map {res => res should equal(Some(user))} + resFuture map { res => res should equal(Some(user)) } } "successfully store a project" in { val resFuture = redisCache.putProjectADM(project) - resFuture map { res => res should equal(true)} + resFuture map { res => res should equal(true) } } "successfully retrieve a project by IRI" in { val resFuture = redisCache.getProjectADM(ProjectIdentifierADM(maybeIri = Some(project.id))) - resFuture map { res => res should equal(Some(project))} + resFuture map { res => res should equal(Some(project)) } } "successfully retrieve a project by SHORTNAME" in { val resFuture = redisCache.getProjectADM(ProjectIdentifierADM(maybeShortname = Some(project.shortname))) - resFuture map { res => res should equal(Some(project))} + resFuture map { res => res should equal(Some(project)) } } "successfully retrieve a project by SHORTCODE" in { val resFuture = redisCache.getProjectADM(ProjectIdentifierADM(maybeShortcode = Some(project.shortcode))) - resFuture map { res => res should equal(Some(project))} + resFuture map { res => res should equal(Some(project)) } } } }