zoedsoupe / peri

Elixir library for declarative data description and validation
MIT License
84 stars 2 forks source link

Allow structs as data #6

Closed dvv closed 3 months ago

dvv commented 3 months ago

Description Ability to validate potentially deeply nested structs is wanted

Motivation Ability to validate data which does not implement Access protocol

Alternatives Peri.validate(schema, data |> Jason.encode!() |> Jason.decode!()) to switch to maps which do implement Access protocol

Additional Context A dirty fix is to prepend https://github.com/zoedsoupe/peri/blob/main/lib/peri.ex#L335 with kinda:

  defp get_enumerable_value(struct, key) when is_struct(struct), do: get_enumerable_value(Map.from_struct(struct), key)

However, the authors may see another way.

TIA

zoedsoupe commented 3 months ago

Hello! Thank you for suggesting enhancements. Just for the sake my curiosity, could you provide some examples of usage of defining schemas and validations with structs?

I would like to think better on how to support them and if support them would break any of the features for now.

dvv commented 3 months ago

I'm exploring the following way of writing handlers:


defmodule ATypedFooResult do
  defstruct [:id, :name, :description, :created_at, :updated_at]
end

import Peri

defschema :uuid, {:string, {:regex, ~r/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/}}
defschema :foo, %{
  id: {:required, get_schema(:uuid)},
  name: {:required, :string},
  description: :string,
  created_at: {:required, :datetime},
  updated_at: {:required, :datetime},
}
defschema :args_schema, %{
  id: {:required, get_schema(:uuid)},
}
defschema :result_schema, get_schema(:foo)

post "/foo" do
  # validate input
  args = case validate(get_schema(:args_schema), conn.params) do
    {:ok, args} -> args
    {:error, errors} -> raise "Invalid arguments"
  end
  # do the stuff
  result = API.foo(args)
  # validate output (to conform API contract)
  # NB: the following is not possible ATM
  %ATypedFooResult{} = result = case validate(get_schema(:result_schema), result) do
    {:ok, result} -> result
    {:error, errors} -> raise "Invalid result"
  end
  # respond with 200 result
  ...
end
dvv commented 3 months ago

Well, own structs may be made accessible via https://gist.github.com/andykingking/4982353b8c69ea301c698e97f6d34635

zoedsoupe commented 3 months ago

ok, addressing that!

zoedsoupe commented 3 months ago

fixed

dvv commented 3 months ago

Hi! Am getting

(CaseClauseError) no case clause matching: {:error, [%Peri.Error{path:
[:email], key: :email, content: %{}, message: "is required", errors: nil}]}
    (peri 0.2.7) deps/peri/lib/peri.ex:264: Peri.validate/2

on the current master. Please consider. TIA

On Fri, Aug 2, 2024 at 4:15 AM Zoey de Souza Pessanha < @.***> wrote:

fixed

— Reply to this email directly, view it on GitHub https://github.com/zoedsoupe/peri/issues/6#issuecomment-2264308342, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABTTB2WCYFOVTOYTIHHLOTZPLMT3AVCNFSM6AAAAABLXBBZG6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRUGMYDQMZUGI . You are receiving this because you authored the thread.Message ID: @.***>

zoedsoupe commented 3 months ago

oh, i forget to handle nested schemas errors on raw data structures! just fixed