Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix(candeletecardinalities): return canDoResponse of false instead of…
… throwing an exception for inherited cardinalities (DEV-314) (#1966)

* return false instead of throwing exception

* add tests

* add missing dependencies after reformatting

* Update doc

* check for usage of inherited properties

* remove code smells

* Improve code after review

* fix failing tests
  • Loading branch information
irinaschubert committed Dec 16, 2021
1 parent 2cebac7 commit 55b5d4b
Show file tree
Hide file tree
Showing 2 changed files with 399 additions and 107 deletions.
Expand Up @@ -52,7 +52,6 @@ object Cardinalities {
)(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)
Expand Down Expand Up @@ -106,9 +105,20 @@ object Cardinalities {
cardinalitiesToDelete: Map[SmartIri, Cardinality.KnoraCardinalityInfo] =
deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema).directCardinalities

_ = cardinalitiesToDelete.foreach(p =>
isCardinalityDefinedOnClass(cacheData, p._1, p._2, internalClassIri, internalOntologyIri)
)
isDefinedOnClassList: List[Boolean] <- Future
.sequence(cardinalitiesToDelete.map { p =>
for {
isDefined: Boolean <- isCardinalityDefinedOnClass(
cacheData,
p._1,
p._2,
internalClassIri,
internalOntologyIri
)
} yield isDefined
}.toList)

atLeastOneCardinalityNotDefinedOnClass: Boolean = isDefinedOnClassList.contains(false)

// Check if property is used in resources of this class

Expand Down Expand Up @@ -176,7 +186,9 @@ object Cardinalities {
throw BadRequestException(msg)
}
)
} yield CanDoResponseV2(!propertyIsUsed)

// response is true only when property is not used in data and cardinality is defined directly on that class
} yield CanDoResponseV2(!propertyIsUsed && !atLeastOneCardinalityNotDefinedOnClass)
}

/**
Expand Down Expand Up @@ -250,9 +262,24 @@ object Cardinalities {
cardinalitiesToDelete: Map[SmartIri, Cardinality.KnoraCardinalityInfo] =
deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema).directCardinalities

_ = cardinalitiesToDelete.foreach(p =>
isCardinalityDefinedOnClass(cacheData, p._1, p._2, internalClassIri, internalOntologyIri)
)
isDefinedOnClassList: List[Boolean] <- Future
.sequence(cardinalitiesToDelete.map { p =>
for {
isDefined: Boolean <- isCardinalityDefinedOnClass(
cacheData,
p._1,
p._2,
internalClassIri,
internalOntologyIri
)
} yield isDefined
}.toList)

_ = if (isDefinedOnClassList.contains(false)) {
throw BadRequestException(
"The cardinality is not defined directly on the class and cannot be deleted."
)
}

// Check if property is used in resources of this class

Expand Down Expand Up @@ -484,25 +511,6 @@ object Cardinalities {
.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.
*
Expand All @@ -511,7 +519,7 @@ object Cardinalities {
* @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.
* @return `true` if the cardinality is defined on the class, `false` otherwise
*/
def isCardinalityDefinedOnClass(
cacheData: Cache.OntologyCacheData,
Expand All @@ -521,30 +529,58 @@ object Cardinalities {
internalOntologyIri: SmartIri
)(implicit ec: ExecutionContext): Future[Boolean] = {
val currentOntologyState: ReadOntologyV2 = cacheData.ontologies(internalOntologyIri)
val currentClassState: ClassInfoContentV2 = currentOntologyState.classes

val readClassInfo: ReadClassInfoV2 = currentOntologyState.classes
.getOrElse(
internalClassIri,
throw BadRequestException(
s"Class ${internalClassIri} does not exist"
s"Class $internalClassIri does not exist"
)
)
.entityInfoContent
val existingCardinality = currentClassState.directCardinalities
.getOrElse(
propertyIri,

// if cardinality is inherited, it's not directly defined on that class
if (readClassInfo.inheritedCardinalities.keySet.contains(propertyIri)) {
return FastFuture.successful(false)
}

val currentClassState: ClassInfoContentV2 = readClassInfo.entityInfoContent
val existingCardinality = currentClassState.directCardinalities.get(propertyIri)
existingCardinality match {
case Some(cardinality) =>
if (cardinality.cardinality.equals(cardinalityInfo.cardinality)) {
FastFuture.successful(true)
} else {
FastFuture.failed(
throw BadRequestException(
s"Submitted cardinality for property $propertyIri does not match existing cardinality."
)
)
}
case None =>
throw BadRequestException(
s"Cardinality for property ${propertyIri} is not defined."
s"Submitted cardinality for property $propertyIri is not defined for class $internalClassIri."
)
)
if (existingCardinality.cardinality.equals(cardinalityInfo.cardinality)) {
}

}

/**
* 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"Submitted cardinality for property ${propertyIri} does not match existing cardinality."
s"Class ${submittedClassInfoContentV2.classIri} is not a subclass of ${KnoraBase.Resource.toSmartIri}. $submittedClassInfoContentV2"
)
)
}
}

}

0 comments on commit 55b5d4b

Please sign in to comment.