Skip to content

Instantly share code, notes, and snippets.

@ChristopherDavenport
Last active March 12, 2018 17:30
Show Gist options
  • Select an option

  • Save ChristopherDavenport/7b8e865d936be355641296f35889ae18 to your computer and use it in GitHub Desktop.

Select an option

Save ChristopherDavenport/7b8e865d936be355641296f35889ae18 to your computer and use it in GitHub Desktop.
Baseline Validation Example
import cats._
import cats.data._
import cats.implicits._
object ValidateMyExistence {
// Say you are writing a password change application.
// You are given a username, an old password, and a new password
case class PasswordChangeRequest(username: String, oldPassword: String, newPassword: String)
// We have a set of rules we would like to enforce on this data,
// and would like to ensure that all of them are correct,
// or we will return a list of all of the failures
// Rules:
// 1. Username MUST be all lowercase
// 2. Username MUST NOT be empty
// 3. Passwords MUST be atleast 8 characters long
// 4. Passwords MUST contain a Number in the first 8 characters
// 5. Passwords MUST contain a Letter in the first 8 characters
// 6. Passwords MUST NOT contain characters ' \ or :
class Username private[ValidateMyExistence] (v: String){
val value: String = v
}
private def validateUsername(username: String): ValidatedNel[String, Username] =
(usernameLowercase(username), usernameNotEmpty(username))
.mapN((_, _) => new Username(username))
private def usernameLowercase(username: String): ValidatedNel[String, Unit] =
Validated.condNel[String, Unit](
username.matches(".*[a-z]+.*"),
(),
"Username Not All Lowercase"
)
private def usernameNotEmpty(username: String): ValidatedNel[String, Unit] =
Validated.condNel[String, Unit](
!username.isEmpty,
(),
"Username is Empty"
)
class Password private[ValidateMyExistence] (v: String){
val value: String = v
}
private sealed trait PasswordType
private object PasswordType {
implicit val showPassword: Show[PasswordType] = Show.show[PasswordType]{
case OldPassword => "Old Password"
case NewPassword => "New Password"
}
case object OldPassword extends PasswordType
case object NewPassword extends PasswordType
}
private def validatePassword(password: String, passwordType: PasswordType): ValidatedNel[String, Password] =
(
passwordLongerThanEight(password, passwordType),
numberInFirstEightChars(password, passwordType),
letterInFirstEightChars(password, passwordType),
doesNotContainInvalidCharacters(password, passwordType)
).mapN((_, _, _, _) => new Password(password))
private def passwordLongerThanEight(password: String, passwordType: PasswordType): ValidatedNel[String, Unit] =
Validated.condNel[String, Unit](
password.length >= 8,
(),
show"PasswordType: $passwordType - Password Not Longer Than 8 Characters"
)
private def numberInFirstEightChars(password: String, passwordType: PasswordType): ValidatedNel[String, Unit] =
Validated.condNel[String, Unit](
password.take(8).matches(".*\\d+.*"),
(),
show"PasswordType: $passwordType - No Number in First 8 Characters"
)
private def letterInFirstEightChars(password: String, passwordType: PasswordType): ValidatedNel[String, Unit] =
Validated.condNel(
password.take(8).matches(".*[a-zA-Z]+.*"),
(),
show"PasswordType: $passwordType - No Alpha Character in First 8 Characters"
)
private def doesNotContainInvalidCharacters(s: String, passwordType: PasswordType): ValidatedNel[String, Unit] = (
Validated.condNel[String, Unit](!s.contains("\""), (), show"""PasswordType: $passwordType - Contains Invalid Character: '"'"""),
Validated.condNel[String, Unit](!s.contains("\'"), (), show"""PasswordType: $passwordType - Contains Invalid Character: "'" """),
Validated.condNel[String, Unit](!s.contains(":"), (), show"""PasswordType: $passwordType - Contains Invalid Character: ':'""")
).mapN((_, _, _) => ())
case class ValidatedPasswordChangeRequest(username: Username, oldPassword: Password, newPassword: Password)
def validatePasswordChangeRequest(p: PasswordChangeRequest): ValidatedNel[String, ValidatedPasswordChangeRequest] =
(
validateUsername(p.username),
validatePassword(p.oldPassword, PasswordType.OldPassword),
validatePassword(p.newPassword, PasswordType.NewPassword)
).mapN(ValidatedPasswordChangeRequest)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment