Skip to content

Commit 3d5f802

Browse files
author
Benjamin Geer
authored
fix(api-v2): Don't allow an invalid cardinality on a boolean property (DSP-1236) (#1788)
1 parent 62cd89d commit 3d5f802

2 files changed

Lines changed: 152 additions & 142 deletions

File tree

‎webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala‎

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,13 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
9797
superClassOfRelations: Map[SmartIri, Set[SmartIri]],
9898
subPropertyOfRelations: Map[SmartIri, Set[SmartIri]],
9999
guiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]],
100-
standoffProperties: Set[SmartIri])
100+
standoffProperties: Set[SmartIri]) {
101+
lazy val allPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = ontologies.values
102+
.flatMap(_.properties.map {
103+
case (propertyIri, readPropertyInfo) => propertyIri -> readPropertyInfo.entityInfoContent
104+
})
105+
.toMap
106+
}
101107

102108
/**
103109
* Receives a message of type [[OntologiesResponderRequestV2]], and returns an appropriate response message.
@@ -163,7 +169,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
163169
*/
164170
private def loadOntologies(featureFactoryConfig: FeatureFactoryConfig,
165171
requestingUser: UserADM): Future[SuccessResponseV2] = {
166-
for {
172+
val loadOntologiesFuture: Future[SuccessResponseV2] = for {
167173
_ <- Future {
168174
if (!(requestingUser.id == KnoraSystemInstances.Users.SystemUser.id || requestingUser.permissions.isSystemAdmin)) {
169175
throw ForbiddenException(s"Only a system administrator can reload ontologies")
@@ -211,8 +217,19 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
211217
ontologyGraphs: Iterable[OntologyGraph] <- Future.sequence(ontologyGraphResponseFutures)
212218

213219
_ = makeOntologyCache(allOntologyMetadata, ontologyGraphs)
214-
215220
} yield SuccessResponseV2("Ontologies loaded.")
221+
222+
loadOntologiesFuture.recover {
223+
case exception: Throwable =>
224+
exception match {
225+
case inconsistentRepositoryDataException: InconsistentRepositoryDataException =>
226+
log.error(inconsistentRepositoryDataException.message)
227+
SuccessResponseV2(
228+
s"An error occurred when loading ontologies: ${inconsistentRepositoryDataException.message}")
229+
230+
case other => throw other
231+
}
232+
}
216233
}
217234

218235
/**
@@ -895,24 +912,16 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
895912
}
896913
}
897914

898-
// A cardinality on a property with a boolean object must be 1 or 0-1.
899-
900-
val invalidCardinalitiesOnBooleanProps: Set[SmartIri] = directCardinalities.filter {
901-
case (propertyIri, knoraCardinalityInfo) =>
902-
val propertyObjectClassConstraint: SmartIri = allPropertyDefs(propertyIri).requireIriObject(
903-
OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri,
904-
throw InconsistentRepositoryDataException(
905-
s"Property $propertyIri has no knora-base:objectClassConstraint")
906-
)
907-
propertyObjectClassConstraint == OntologyConstants.KnoraBase.BooleanValue.toSmartIri &&
908-
!(knoraCardinalityInfo.cardinality == Cardinality.MustHaveOne || knoraCardinalityInfo.cardinality == Cardinality.MayHaveOne)
909-
}.keySet
910-
911-
if (invalidCardinalitiesOnBooleanProps.nonEmpty) {
912-
throw InconsistentRepositoryDataException(
913-
s"Class $classIri has one or more invalid cardinalities on boolean properties: ${invalidCardinalitiesOnBooleanProps
914-
.mkString(", ")}")
915-
}
915+
// Check for invalid cardinalities on boolean properties.
916+
checkForInvalidBooleanCardinalities(
917+
classIri = classIri,
918+
directCardinalities = directCardinalities,
919+
allPropertyDefs = allPropertyDefs,
920+
schemaForErrors = InternalSchema,
921+
errorFun = { msg: String =>
922+
throw InconsistentRepositoryDataException(msg)
923+
}
924+
)
916925

917926
// All its base classes with Knora IRIs must also be resource classes.
918927
for (baseClass <- classDef.subClassOf) {
@@ -1023,6 +1032,42 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
10231032
}
10241033
}
10251034

1035+
/**
1036+
* Checks for invalid cardinalities on boolean properties.
1037+
*
1038+
* @param classIri the class IRI.
1039+
* @param directCardinalities the cardinalities directly defined on the class.
1040+
* @param allPropertyDefs all property definitions.
1041+
*/
1042+
def checkForInvalidBooleanCardinalities(classIri: SmartIri,
1043+
directCardinalities: Map[SmartIri, KnoraCardinalityInfo],
1044+
allPropertyDefs: Map[SmartIri, PropertyInfoContentV2],
1045+
schemaForErrors: OntologySchema,
1046+
errorFun: String => Nothing): Unit = {
1047+
// A cardinality on a property with a boolean object must be 1 or 0-1.
1048+
1049+
val invalidCardinalitiesOnBooleanProps: Set[SmartIri] = directCardinalities.filter {
1050+
case (propertyIri, knoraCardinalityInfo) =>
1051+
val objectClassConstraintIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri
1052+
1053+
val propertyObjectClassConstraint: SmartIri = allPropertyDefs(propertyIri).requireIriObject(
1054+
objectClassConstraintIri,
1055+
errorFun(s"Property ${propertyIri
1056+
.toOntologySchema(schemaForErrors)} has no ${objectClassConstraintIri.toOntologySchema(schemaForErrors)}")
1057+
)
1058+
1059+
propertyObjectClassConstraint == OntologyConstants.KnoraBase.BooleanValue.toSmartIri &&
1060+
!(knoraCardinalityInfo.cardinality == Cardinality.MustHaveOne || knoraCardinalityInfo.cardinality == Cardinality.MayHaveOne)
1061+
}.keySet
1062+
1063+
if (invalidCardinalitiesOnBooleanProps.nonEmpty) {
1064+
errorFun(
1065+
s"Class ${classIri.toOntologySchema(schemaForErrors).toSparql} has one or more invalid cardinalities on boolean properties: ${invalidCardinalitiesOnBooleanProps
1066+
.map(_.toOntologySchema(schemaForErrors).toSparql)
1067+
.mkString(", ")}")
1068+
}
1069+
}
1070+
10261071
/**
10271072
* Constructs a map of property IRIs to [[ReadPropertyInfoV2]] instances, based on property definitions loaded from the
10281073
* triplestore.
@@ -2504,6 +2549,17 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
25042549
case None => ()
25052550
}
25062551

2552+
// Check for invalid cardinalities on boolean properties.
2553+
checkForInvalidBooleanCardinalities(
2554+
classIri = internalClassDef.classIri,
2555+
directCardinalities = internalClassDef.directCardinalities,
2556+
allPropertyDefs = cacheData.allPropertyDefs,
2557+
schemaForErrors = ApiV2Complex,
2558+
errorFun = { msg: String =>
2559+
throw BadRequestException(msg)
2560+
}
2561+
)
2562+
25072563
(classDefWithAddedLinkValueProps, cardinalitiesForClassWithInheritance)
25082564
}
25092565

0 commit comments

Comments
 (0)