Data validation

Hi *, I’m trying to validate the presence of some keys in a Map, but I’m not sure how to do it right.
First problem, I want to get/3 a key, and have an empty string if the key doesn’t exist of is nil. This is what I wrote but honestly I don’t like it at all.

empty_if_nil( maxwell |> Map.get(:body, "") )
defp empty_if_nil(nil), do: ""
defp empty_if_nil(val), do: val

The other problem, I want to check if the maxwell map has certain values with actual content inside (so not nil or empty string), I see there are some interesting libraries for that, but it seems a little too much for what I can do.

Partial explanation: I’m using Maxwell (https://github.com/zhongwencool/maxwell) to make signed requests against a service. For the signature to be valid, it needs to be done as the last step of the process.
This is a typical Maxwell request:

url(request_url_string_or_char_list)
  |> query(request_query_map)
  |> headers(request_headers_map)
  |> opts(request_opts_keyword_list)
  |> body(request_body_term)
  |> YourClient.{http_method}!

The request should be signed right after calling the body/2 function and set all the HTTP headers needed. If I sign the request any time before, the request will not authenticate, as the signature is generated using the request body.
Any suggestion is very welcome.

ngw

1 Like

That method works, although I usually name them sanitize_maxwell_body or so for explicitness. If you do the lookup internally to that as well, like:

sanitize_maxwell_body(maxwell) do
  maxwell
  |> Map.get(:body)
  |> case do
    str when is_binary(str) -> str
    _ -> "" # When it is anything but a string
  end
end

Then in the validation I do things like:

sanitize_maxwell(maxwell) do
  maxwell
  |> sanitize_maxwell_body
  |> sanitize_maxwell_whatever
  |> and more...
end

As for the second part of your post, what kind of things are you wanting to check for? Are you wanting a string parsing library? Are they lists? Etc…? Can you pseudo-code an example up of what you are wanting? :slight_smile:

1 Like

Hi, thanks for the suggestions.
I’m parsing an HTTP request, prettmy much like this

%Maxwell{body: nil, headers: %{}, method: nil, opts: [], status: nil, url: “”}

method and url, for example, can’t be empty or nil - it doesn’t make sense to sign a request without HTTP verb or url, some others, that can be empty, change the signature when changed, the body for example.
I don’t have a clear idea on how I want to deal with this - it’s very obvious that if you change the request body the signature is not valid anymore, being partially based on the body itself - so I’m mostly asking for guidance on the most idiomatic ways to deal with this.

Thanks in advance
ngw

I’m not sure if this is idomatic or not, but you can take advantage of Elixir’s dynamic nature.

It is possible to assign functions to elements of any struct.

You’d create a validator struct that had a function for every key in the structure. You then
Enum over the keys of the struct and pull functions from the validator struct to transform
the struct.

This is a construct I fall back to often. When I need to do essentially the same thing but just slightly different in various places, I create a map of functions that are keyed on the place name.

This can clearly be abused to enable OO style thinking, but I think it helps break the “disassemble/reassemble” thinking pattern. Think about data and transforms, every call to Map.put/get is really Map.new.

FWIW, I think this is more or less the model that Ecto changesets uses, but I’m not that familiar with Ecto.

Using the above ‘sanitize’ style I would do those just via pattern matching on the function heads, that way it would crash (which you can catch if you want) if it does not match. So match out a nil, match out a "", or just test if it is binary with a length at least of 1 all at once. Matching is very powerful perfect for these purposes. Pipeline it and make sure your signer is last in the pipeline before it is sent back. :slight_smile:

I’ve tried but surely messed things up, and as much as I like the idea I don’t really like how I implemented it :slight_smile:

Probably it’s because I’ve tried to keep the {:result, :body} structure I see is used everywhere, but I’m a little stuck now on how to improve it, how can I pipe and match the result of the previous operation? As you can see I ignore whatever validate_url returns which is very bad and kind of the point of the whole thing.

ngw

Very interesting, thank you, I think I understood what you mean and I probably so that in a library, but can’t remember where. Do you have any example?

ngw