Copyright(c) digitally induced GmbH 2020
Safe HaskellNone

IHP.ValidationSupport.ValidateField

Description

Use validateField and validateFieldIO together with the validation functions to do simple validations.

Also take a look at validateIsUnique for e.g. checking that an email is unique.

Synopsis

Documentation

type Validator valueType = valueType -> ValidatorResult Source #

A function taking some value and returning a ValidatorResult

>>> Validator Text
Text -> ValidatorResult
>>> Validator Int
Int -> ValidatorResult

validateField :: forall field fieldValue validator model. (KnownSymbol field, HasField field model fieldValue, HasField "meta" model MetaBag, SetField "meta" model MetaBag) => Proxy field -> Validator fieldValue -> model -> model Source #

Validates a record field using a given validator function.

When the validation fails, the validation error is saved inside the meta :: MetaBag field of the record. You can retrieve a possible validation error using getValidationFailure.

Example: nonEmpty validation for a record

let project :: Project = newRecord
project
    |> validateField #name nonEmpty
    |> getValidationFailure #name -- Just "This field cannot be empty"


project
    |> set #name "Hello World"
    |> validateField #name nonEmpty
    |> getValidationFailure #name -- Nothing

Example: Using ifValid for branching

let project :: Project = newRecord

project
    |> validateField #name nonEmpty
    |> ifValid \case
        Left project -> do
            putStrLn "Invalid project. Please try again"
        Right project -> do
            putStrLn "Project is valid. Saving to database."
            createRecord project

type ValidatorIO value = value -> IO ValidatorResult Source #

A function taking some value and returning a 'IO ValidatorResult'

>>> ValidatorIO Text
Text -> IO ValidatorResult
>>> ValidatorIO Int
Int -> IO ValidatorResult

validateFieldIO :: forall field model fieldValue. (?modelContext :: ModelContext, KnownSymbol field, HasField field model fieldValue, HasField "meta" model MetaBag, SetField "meta" model MetaBag) => Proxy field -> ValidatorIO fieldValue -> model -> IO model Source #

Validates a record field using a given validator function.

The same as validateField, but works with IO and can e.g. access the database.

When the validation fails, the validation error is saved inside the meta :: MetaBag field of the record. You can retrieve a possible validation error using getValidationFailure.

withCustomErrorMessage :: Text -> (value -> ValidatorResult) -> value -> ValidatorResult Source #

Overrides the error message of a given validator function.

>>> (nonEmpty |> withCustomErrorMessage "Custom error message") ""
Failure "Custom error message"
>>> (isEmail |> withCustomErrorMessage "We only accept valid email addresses") "not valid email"
Failure "We only accept valid email addresses"

validateAny :: [value -> ValidatorResult] -> value -> ValidatorResult Source #

Validates that value passes at least one of the given validators

>>> "ihp@example.com" |> validateAny([isEmptyValue, isEmail])
Success
>>> "" |> validateAny([isEmptyValue, isEmail])
Success
>>> "no spam plz" |> validateAny([empty, isEmail])
Failure "did not pass any validators"

validateAll :: [value -> ValidatorResult] -> value -> ValidatorResult Source #

Validates that value passes all of the given validators

In case of multiple failures, the first Failure is returned.

>>> 2016 |> validateAll([isGreaterThan(1900), isLessThan(2020)])
Success
>>> 1899 |> validateAll([isGreaterThan(1900), isLessThan(2020)])
Failure "has to be greater than 1900"

nonEmpty :: MonoFoldable value => value -> ValidatorResult Source #

Validates that value is not empty

>>> nonEmpty "hello world"
Success
>>> nonEmpty ""
Failure "This field cannot be empty"
>>> nonEmpty (Just "hello")
Success
>>> nonEmpty Nothing
Failure "This field cannot be empty"

isEmptyValue :: MonoFoldable value => value -> ValidatorResult Source #

Validates that value is empty

>>> isEmptyValue "hello world"
Failure "This field must be empty"
>>> ieEmptyValue ""
Success
>>> isEmptyValue (Just "hello")
Failure "This field must be empty"
>>> isEmptyValue Nothing
Success

isPhoneNumber :: Text -> ValidatorResult Source #

Validates that value looks like a phone number

Values needs to start with + and has to have atleast 5 characters

>>> isPhoneNumber "1337"
Failure ".."
>>> isPhoneNumber "+49123456789"
Success

isEmail :: Text -> ValidatorResult Source #

Validates that value is an email address

The validation is not meant to be compliant with RFC 822. Its purpose is to reject obviously invalid values without false-negatives.

>>> isEmail "marc@digitallyinduced.com"
Success
>>> isEmail "marc@secret.digitallyinduced.com" -- subdomains are fine
Success
>>> isEmail "ॐ@मणिपद्मे.हूँ"
Success
>>> isEmail "marc@localhost" -- Although discouraged by ICANN, dotless TLDs are legal. See https://www.icann.org/news/announcement-2013-08-30-en
Success
>>> isEmail "loremipsum"
Failure "is not a valid email"
>>> isEmail "A@b@c@domain.com"
Failure "is not a valid email"

isInRange :: (Show value, Ord value) => (value, value) -> value -> ValidatorResult Source #

Validates that value is between min and max

>>> isInRange (0, 10) 5
Success
>>> isInRange (0, 10) 0
Success
>>> isInRange (0, 10) 1337
Failure "has to be between 0 and 10"
>>> let isHumanAge = isInRange (0, 100)
>>> isHumanAge 22
Success

isLessThan :: (Show value, Ord value) => value -> value -> ValidatorResult Source #

Validates that value is less than a max value

>>> isLessThan 10 5
Success
>>> isLessThan 10 20
Failure "has to be less than 10"

isGreaterThan :: (Show value, Ord value) => value -> value -> ValidatorResult Source #

Validates that value is greater than a min value

>>> isGreaterThan 10 20
Success
>>> isGreaterThan 10 5
Failure "has to be greater than 10"

hasMaxLength :: Int -> Text -> ValidatorResult Source #

Validates that value has a max length

>>> hasMaxLength 10 "IHP"
Success
>>> hasMaxLength 2 "IHP"
Failure "is longer than 2 characters"

hasMinLength :: Int -> Text -> ValidatorResult Source #

Validates that value has a min length

>>> hasMinLength 2 "IHP"
Success
>>> hasMinLength 10 "IHP"
Failure "is shorter than 10 characters"

isRgbHexColor :: Text -> ValidatorResult Source #

Validates that value is a hex-based rgb color string

>>> isRgbHexColor "#ffffff"
Success
>>> isRgbHexColor "#fff"
Success
>>> isRgbHexColor "rgb(0, 0, 0)"
Failure "is not a valid rgb hex color"

isRgbaHexColor :: Text -> ValidatorResult Source #

Validates that value is a hex-based rgb color string

>>> isRgbaHexColor "#ffffffff"
Success
>>> isRgbaHexColor "#ffff"
Success
>>> isRgbaHexColor "rgb(0, 0, 0, 1)"
Failure "is not a valid rgba hex color"

isHexColor :: Text -> ValidatorResult Source #

Validates that value is a hex-based rgb(a) color string

>>> isHexColor "#ffffff"
Success
>>> isHexColor "#ffffffff"
Success
>>> isHexColor "rgb(0, 0, 0)"
Failure "is not a valid hex color"

isRgbColor :: Text -> ValidatorResult Source #

Validates that value is a rgb() color string

>>> isRgbColor "rgb(255, 0, 0)"
Success
>>> isRgbColor "#f00"
Failure "is not a valid rgb() color"

isRgbaColor :: Text -> ValidatorResult Source #

Validates that value is a rgba() color string

>>> isRgbaColor "rgb(255, 0, 0, 1.0)"
Success
>>> isRgbaColor "#f00f"
Failure "is not a valid rgba() color"

isColor :: Text -> ValidatorResult Source #

Validates that value is a hex-based or rgb(a) color string

>>> isColor "#ffffff"
Success
>>> isColor "rgba(255, 0, 0, 0.5)"
Success
>>> isColor "rgb(0, 0, 0)"
Failure "is not a valid color"

isUrl :: Text -> ValidatorResult Source #

Validates string starts with http:// or https://

>>> isUrl "https://digitallyinduced.com"
Success
>>> isUrl "digitallyinduced.com"
Failure "is not a valid url. It needs to start with http:// or https://"

isInList :: (Eq value, Show value) => [value] -> value -> ValidatorResult Source #