-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #129 from FRosner/issue/128
#128 custom constraints (issue/128)
- Loading branch information
Showing
3 changed files
with
120 additions
and
0 deletions.
There are no files selected for viewing
37 changes: 37 additions & 0 deletions
37
src/main/scala/de/frosner/ddq/constraints/CustomConstraint.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package de.frosner.ddq.constraints | ||
|
||
import org.apache.spark.sql.DataFrame | ||
|
||
import CustomConstraint.{FailureMsg, SuccessMsg} | ||
|
||
import scala.util.Try | ||
|
||
case class CustomConstraint(name: String, | ||
constraintFunction: DataFrame => Either[FailureMsg, SuccessMsg] | ||
) extends Constraint { | ||
|
||
val fun = (df: DataFrame) => { | ||
val tryFun = Try(constraintFunction(df)) | ||
val messagePrefix = s"Custom constraint '$name'" | ||
val message = tryFun.map { | ||
case Left(failureMsg) => s"$messagePrefix failed: $failureMsg" | ||
case Right(successMsg) => s"$messagePrefix succeeded: $successMsg" | ||
}.recover { | ||
case throwable => s"$messagePrefix errored: $throwable" | ||
}.get | ||
val status = ConstraintUtil.tryToStatus[Either[FailureMsg, SuccessMsg]](tryFun, _.isRight) | ||
CustomConstraintResult(this, message, status) | ||
} | ||
|
||
} | ||
|
||
case class CustomConstraintResult(constraint: CustomConstraint, | ||
message: String, | ||
status: ConstraintStatus) extends ConstraintResult[CustomConstraint] | ||
|
||
object CustomConstraint { | ||
|
||
type SuccessMsg = String | ||
type FailureMsg = String | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
src/test/scala/de/frosner/ddq/constraints/CustomConstraintTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package de.frosner.ddq.constraints | ||
|
||
import de.frosner.ddq.core.Check | ||
import de.frosner.ddq.testutils.{SparkContexts, TestData} | ||
import org.apache.spark.sql.AnalysisException | ||
import org.scalatest.{FlatSpec, Matchers} | ||
|
||
class CustomConstraintTest extends FlatSpec with Matchers with SparkContexts { | ||
|
||
"A CustomConstraint" should "succeed if the function returns a success message" in { | ||
val constraintName = "name" | ||
val successMsg = "success" | ||
val check = Check(TestData.makeNullableStringDf(spark, List("a"))).custom(constraintName, { | ||
df => Right(successMsg) | ||
}) | ||
val constraint = check.constraints.head | ||
val result = CustomConstraintResult( | ||
constraint = constraint.asInstanceOf[CustomConstraint], | ||
message = s"Custom constraint '$constraintName' succeeded: $successMsg", | ||
status = ConstraintSuccess | ||
) | ||
check.run().constraintResults shouldBe Map(constraint -> result) | ||
} | ||
|
||
it should "fail if the function returns a failure message" in { | ||
val constraintName = "name" | ||
val failureMsg = "failure" | ||
val check = Check(TestData.makeNullableStringDf(spark, List("a"))).custom(constraintName, { | ||
df => Left(failureMsg) | ||
}) | ||
val constraint = check.constraints.head | ||
val result = CustomConstraintResult( | ||
constraint = constraint.asInstanceOf[CustomConstraint], | ||
message = s"Custom constraint '$constraintName' failed: $failureMsg", | ||
status = ConstraintFailure | ||
) | ||
check.run().constraintResults shouldBe Map(constraint -> result) | ||
} | ||
|
||
it should "error if the function throws an exception" in { | ||
val constraintName = "name" | ||
val exception = new Exception() | ||
val check = Check(TestData.makeNullableStringDf(spark, List("a"))).custom(constraintName, { | ||
df => throw exception | ||
}) | ||
val constraint = check.constraints.head | ||
val result = check.run().constraintResults(constraint) | ||
result match { | ||
case CustomConstraintResult( | ||
customConstraint: CustomConstraint, | ||
"Custom constraint 'name' errored: java.lang.Exception", | ||
constraintError: ConstraintError | ||
) => { | ||
constraintError.throwable shouldBe exception | ||
} | ||
} | ||
} | ||
|
||
} |