From 6a22efce1a921ea49ce5b37028d98dcb3d956a2a Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 28 May 2025 22:04:54 +0200 Subject: [PATCH] feat: add custom email type for validation --- lib/membership/email.ex | 34 +++++++++++++++++++++++++++++++++ lib/membership/property.ex | 3 ++- lib/membership/property_type.ex | 2 +- priv/repo/seeds.exs | 22 ++++++--------------- 4 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 lib/membership/email.ex diff --git a/lib/membership/email.ex b/lib/membership/email.ex new file mode 100644 index 0000000..eacd548 --- /dev/null +++ b/lib/membership/email.ex @@ -0,0 +1,34 @@ +defmodule Mv.Membership.Email do + @constraints [ + match: ~r/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/, + trim?: true, + min_length: 5, + max_length: 254 + ] + + use Ash.Type.NewType, + subtype_of: :string, + constraints: @constraints + + @impl true + def cast_input(value, _) when is_binary(value) do + value = if @constraints[:trim?], do: String.trim(value), else: value + + cond do + @constraints[:min_length] && String.length(value) < @constraints[:min_length] -> + :error + + @constraints[:max_length] && String.length(value) > @constraints[:max_length] -> + :error + + @constraints[:match] && !Regex.match?(@constraints[:match], value) -> + :error + + true -> + {:ok, value} + end + end + + @impl true + def cast_input(_, _), do: :error +end diff --git a/lib/membership/property.ex b/lib/membership/property.ex index 4e96731..0bd5eab 100644 --- a/lib/membership/property.ex +++ b/lib/membership/property.ex @@ -23,7 +23,8 @@ defmodule Mv.Membership.Property do boolean: [type: :boolean], date: [type: :date], integer: [type: :integer], - string: [type: :string] + string: [type: :string], + email: [type: Mv.Membership.Email] ] ] end diff --git a/lib/membership/property_type.ex b/lib/membership/property_type.ex index 9bf16c9..8e42fa6 100644 --- a/lib/membership/property_type.ex +++ b/lib/membership/property_type.ex @@ -19,7 +19,7 @@ defmodule Mv.Membership.PropertyType do attribute :name, :string, allow_nil?: false, public?: true attribute :value_type, :atom, - constraints: [one_of: [:string, :integer, :boolean, :date]], + constraints: [one_of: [:string, :integer, :boolean, :date, :email]], allow_nil?: false, description: "Definies the datatype `Property.value` is interpreted as" diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 27daf36..39327d0 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -2,13 +2,6 @@ # # mix run priv/repo/seeds.exs # -# Inside the script, you can read and write to any of your -# repositories directly: -# -# Mv.Repo.insert!(%Mv.SomeSchema{}) -# -# We recommend using the bang functions (`insert!`, `update!` -# and so on) as they will fail if something goes wrong. alias Mv.Membership @@ -43,18 +36,15 @@ for attrs <- [ }, %{ name: "Email", - value_type: :string, + value_type: :email, description: "Email-Adresse des Mitglieds", immutable: true, required: true } ] do - # upsert?: true sorgt dafür, dass bei bestehendem Namen kein Fehler, - # sondern ein Update (hier effektiv No-Op) ausgeführt wird - {:ok, _} = - Membership.create_property_type( - attrs, - upsert?: true, - upsert_identity: :unique_name - ) + Membership.create_property_type!( + attrs, + upsert?: true, + upsert_identity: :unique_name + ) end