Last active
January 13, 2023 23:40
-
-
Save sheridanchris/6d60f50e9782106ac999dee2c73031b4 to your computer and use it in GitHub Desktop.
Validation problem details w/ Falco & Validus
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
type ProblemDetails = { | |
Type: string | |
Title: string | |
Status: int | |
Detail: string | |
Instance: string | |
Errors: Map<string, string list> | |
} | |
[<RequireQualifiedAccess>] | |
module ProblemDetails = | |
let createValidationProblemDetails instance errors = { | |
Type = "https://httpstatuses.com/400" | |
Title = "One or more validation errors occured." | |
Status = 400 | |
Detail = "Please refer to the errors property for additional details." | |
Instance = instance | |
Errors = errors | |
} | |
[<RequireQualifiedAccess>] | |
module Request = | |
let mapValidateJson | |
(validator: 'a -> ValidationResult<'b>) | |
(onSuccess: 'b -> HttpHandler) | |
(onValidationErrors: ValidationErrors -> HttpHandler) | |
= | |
let handleOk (record: 'a) : HttpHandler = | |
match validator record with | |
| Ok result -> onSuccess result | |
| Error validationErrors -> onValidationErrors validationErrors | |
Request.mapJson handleOk | |
[<RequireQualifiedAccess>] | |
module Response = | |
let validationProblemDetails (instance: string) (errors: ValidationErrors) : HttpHandler = | |
errors | |
|> ValidationErrors.toMap | |
|> ProblemDetails.createValidationProblemDetails instance | |
|> fun response -> Response.withStatusCode 400 >> Response.ofJson response |
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
type BudgetName = private BudgetName of string | |
type PositiveDecimal = private PositiveDecimal of decimal | |
[<RequireQualifiedAccess>] | |
module BudgetName = | |
let value (BudgetName name) = name | |
let create name = | |
let validator = Check.String.notEmpty <+> Check.String.lessThanLen 150 | |
validate { | |
let! name = validator "Budget name" name | |
return BudgetName name | |
} | |
[<RequireQualifiedAccess>] | |
module PositiveDecimal = | |
let value (PositiveDecimal decimal) = decimal | |
let create value = validate { | |
let! name = Check.Decimal.greaterThanOrEqualTo 0m "amount" value | |
return PositiveDecimal name | |
} | |
module CreateBudget = | |
type CreateBudgetRequest = { Name: string; MonthlyIncome: decimal } | |
type ValidatedCreateBudgetRequest = { | |
Name: BudgetName | |
MonthlyIncome: PositiveDecimal | |
} | |
let validateRequest (createBudgetRequest: CreateBudgetRequest) = validate { | |
let! budgetName = BudgetName.create createBudgetRequest.Name | |
let! monthlyIncome = PositiveDecimal.create createBudgetRequest.MonthlyIncome | |
return { | |
Name = budgetName | |
MonthlyIncome = monthlyIncome | |
} | |
} | |
let handler (saveBudget: Provider.SaveBudget) : HttpHandler = | |
let createBudget (request: ValidatedCreateBudgetRequest) : HttpHandler = | |
fun ctx -> task { | |
// ... | |
return () | |
} | |
let handleValidationErrors = Response.validationProblemDetails "/budget" | |
Request.mapValidateJson validateRequest createBudget handleValidationErrors |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment