Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input Parsing and error handling #14

Open
CrowdHailer opened this issue Apr 29, 2020 · 4 comments
Open

Input Parsing and error handling #14

CrowdHailer opened this issue Apr 29, 2020 · 4 comments
Labels
enhancement New feature or request Gleam Feature needed in language or stdlib help wanted Extra attention is needed Suggestion PR's will be accepted but not on the Roadmap
Milestone

Comments

@CrowdHailer
Copy link
Contributor

Add an example endpoint that parses input.

This will probably make use of https://github.com/rjdellecese/gleam_decode and ideally the approach would be reusable for multiple input sources. e.g. query strings, JSON, forms

@CrowdHailer CrowdHailer added the Suggestion PR's will be accepted but not on the Roadmap label May 5, 2020
@CrowdHailer
Copy link
Contributor Author

CrowdHailer commented Jun 7, 2020

Running through some examples for input handling.

In all these cases the error type is

pub type Invalid(a) {
  Missing(key: String)
  CastFailure(key: String, help: a)
}

Keep as lean as possible.

In these examples all the custom as_* functions return the same error type, which will end up as the content in help.

// required field
try name = params.find(form, "name")
try name = params.cast(form, as_name)

// optional field
let url = params.optional(form, "url")
try url = params.cast_optional(url, url.parse) |> result.map_error(fn(_) { "not a valid url"})

// or wrapper function that handles mapping the error
try url = params.cast_optional(url, as_url)

// handling a fallback
let url = params.use_fallback(url, Uri(...))
Notes
  • each variable name shows up twice and has a different type in each case, first as a raw string then as a cast value.
  • functions like cast cast_optional need to be given key value for writing debug messages.

Higher level

try name = params.required(from: form, get: "name", cast: as_name)
try url = params.optional(from: form, get: "url", cast: as_url)
try url = params.overridable(from: form, get: "url", cast: as_url, or: "default.com")

Better as_ functions

A params module could define cast functions for strings ints etc

try name = params.required(form, "name", as_string(_, [MinLength(5), Pattern(), MaxLength(30)])

Comment on try syntax

If this change existed

let name = try params.required(...
// instead of
try name = params.required(...

Then you could construct form objects really easily

fn cast_form(form) -> Result(CreateUser, params.Invalid) {
  Ok(CreateUser(
    name: try params.required(form, "name", as_name),
    url: try params.overridable(form, "url", as_url, "home.com")
  ))
} 

@CrowdHailer CrowdHailer reopened this Jun 7, 2020
@CrowdHailer CrowdHailer changed the title Input Parsing Input Parsing and error handling Jun 8, 2020
@CrowdHailer
Copy link
Contributor Author

Wrapping Errors

Instead of wrapper functions that all return the same error type we could have a function for parse_form and then wrap the error once.

fn try_parse_form(form) {
  try ...
  try ...
  FormValues(a: ....)
}

pub fn parse_form(form) {
  try_parse_form(form)
  |> result.map_error()
}

There will be a lot of these function pairs. because there is no way to wrap the error inside the first function.

Currently I prefer writting web wrapper functions, but this could be a lot of wrapper functions and requires more up front work about choosing an error type.

Application Error type

handle functions return Result(Response, Error) The error could be another response but this makes it too easy to encode the error in different ways.

It could a big enum

type ErrorResponse {
  UnprocessableEntity(input.Invalid())
}

fn parse_form(form) {
  try_parse_form(form)
  // big enum
  |> result.map_error(UnprocessableEntity)
}

There is no completeness to assure that Enum is used correctly.
Also there might be alot of time designing each sub type.
Nice to use in parse_form func tho, see example above

Or it could be a special Error type

type ErrorResponse {
  ErrorResponse(type: ErrorType, details: String)
}

can enum all the error types, has only a single place to set the details.

@CrowdHailer CrowdHailer added enhancement New feature or request Gleam Feature needed in language or stdlib help wanted Extra attention is needed labels Jun 10, 2020
@CrowdHailer CrowdHailer added this to the 1.0.0 milestone Jun 10, 2020
@CrowdHailer
Copy link
Contributor Author

@lpil thanks for the feedback on the PR, I have merged version 1 to play with will see how it goes.
Am mulling some of your suggestions for a next update.

I particularly think it might make sense to work on maps because although params come in order the api as designed so far doesn't allow you to pull out a value that is defined multiple times

@lpil
Copy link

lpil commented Jun 11, 2020

Perhaps we could move forward with this and a more complex validation library can be left as an exercise for someone else. Getting 90% of the way there with everything provides the most value now!

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Gleam Feature needed in language or stdlib help wanted Extra attention is needed Suggestion PR's will be accepted but not on the Roadmap
Development

No branches or pull requests

2 participants