Copyright | (c) digitally induced GmbH 2020 |
---|---|
Safe Haskell | None |
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
- type Validator valueType = valueType -> ValidatorResult
- validateField :: forall (field :: Symbol) fieldValue model. (KnownSymbol field, HasField field model fieldValue, HasField "meta" model MetaBag, SetField "meta" model MetaBag) => Proxy field -> Validator fieldValue -> model -> model
- type ValidatorIO value = value -> IO ValidatorResult
- validateFieldIO :: forall (field :: Symbol) 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
- validateMaybe :: (val -> ValidatorResult) -> Maybe val -> ValidatorResult
- withCustomErrorMessage :: Text -> (value -> ValidatorResult) -> value -> ValidatorResult
- validateAny :: [value -> ValidatorResult] -> value -> ValidatorResult
- validateAll :: [value -> ValidatorResult] -> value -> ValidatorResult
- nonEmpty :: IsEmpty value => value -> ValidatorResult
- isEmptyValue :: IsEmpty value => value -> ValidatorResult
- isPhoneNumber :: Text -> ValidatorResult
- isEmail :: Text -> ValidatorResult
- isInRange :: (Show value, Ord value) => (value, value) -> value -> ValidatorResult
- isLessThan :: (Show value, Ord value) => value -> value -> ValidatorResult
- isGreaterThan :: (Show value, Ord value) => value -> value -> ValidatorResult
- isEqual :: (Show value, Eq value) => value -> value -> ValidatorResult
- hasMaxLength :: Int -> Text -> ValidatorResult
- hasMinLength :: Int -> Text -> ValidatorResult
- isRgbHexColor :: Text -> ValidatorResult
- isRgbaHexColor :: Text -> ValidatorResult
- isHexColor :: Text -> ValidatorResult
- isRgbColor :: Text -> ValidatorResult
- isRgbaColor :: Text -> ValidatorResult
- isColor :: Text -> ValidatorResult
- isUrl :: Text -> ValidatorResult
- isInList :: (Eq value, Show value) => [value] -> value -> ValidatorResult
- isTrue :: Bool -> ValidatorResult
- isFalse :: Bool -> ValidatorResult
- matchesRegex :: Text -> Text -> ValidatorResult
- isSlug :: Text -> ValidatorResult
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 :: Symbol) fieldValue 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 :: Symbol) 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
.
validateMaybe :: (val -> ValidatorResult) -> Maybe val -> ValidatorResult Source #
Validate a Maybe field.
Validate a Maybe field using a given validator function. >>> validateMaybe nonEmpty (Just "foo") Success
>>>
validateMaybe nonEmpty (Just "")
Failure "This field cannot be empty"
If the value is Nothing
, the validation will succeed.
>>> validateMaybe nonEmpty Nothing
Success
This function is useful when you want to validate a field that is optional. >>> buildPost :: Post -> Post >>> buildPost post = post >>> |> validateField #title nonEmpty >>> -- Assuming sourceUrl is optional. >>> |> validateField #sourceUrl (validateMaybe nonEmpty)
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 :: IsEmpty 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 :: IsEmpty 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"
isEqual :: (Show value, Eq value) => value -> value -> ValidatorResult Source #
Validates that value is equal to another value
>>>
isEqual "foo" "foo"
Success
>>>
isEqual "foo" "bar"
Failure "has to be equal to \"foo\""
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 "URL must start with http:// or https://"
isTrue :: Bool -> ValidatorResult Source #
Validates that value is True
>>>
isTrue True
Success
>>>
isTrue False
Failure "This field cannot be false"
isFalse :: Bool -> ValidatorResult Source #
Validates that value is False
>>>
isFalse False
Success
>>>
isFalse True
Failure "This field cannot be true"
matchesRegex :: Text -> Text -> ValidatorResult Source #
Validates that value is matched by the regular expression
>>>
matchesRegex "^[0-9]{4}$" "2016"
Success
>>>
matchesRegex "^[0-9]{4}$" "16"
Failure "This field does not match the regular expression \"^[0-9]{4}$\""
>>>
matchesRegex "[0-9]{4}" "xx2016xx"
Success -- regex is missing ^ and $
isSlug :: Text -> ValidatorResult Source #
Validates that value is a valid slug
>>>
isSlug "i-am-a-slug"
Success
>>>
isSlug "I-AM-A-Slug (Copy)"
Failure "is not a valid slug (consisting of only letters, numbers, underscores or hyphens)"