diff --git a/lib/membership/email.ex b/lib/membership/email.ex index 47651f5..730ccd7 100644 --- a/lib/membership/email.ex +++ b/lib/membership/email.ex @@ -8,10 +8,11 @@ defmodule Mv.Membership.Email do addresses according to a standard regex pattern. ## Validation Rules - - Minimum length: 5 characters + - **Optional**: `nil` and empty strings are allowed (custom fields are optional) + - Minimum length: 5 characters (for non-empty values) - Maximum length: 254 characters (RFC 5321 maximum) - Pattern: Standard email format (username@domain.tld) - - Automatic trimming of leading/trailing whitespace + - Automatic trimming of leading/trailing whitespace (empty strings become `nil`) ## Usage This type is used in the CustomFieldValue union type for custom fields with @@ -46,11 +47,18 @@ defmodule Mv.Membership.Email do max_length: @max_length ] + @impl true + def cast_input(nil, _), do: {:ok, nil} + @impl true def cast_input(value, _) when is_binary(value) do value = String.trim(value) cond do + # Empty string after trim becomes nil (optional field) + value == "" -> + {:ok, nil} + String.length(value) < @min_length -> :error diff --git a/test/membership/custom_field_validation_test.exs b/test/membership/custom_field_validation_test.exs index d8a5bd9..a5c1f2d 100644 --- a/test/membership/custom_field_validation_test.exs +++ b/test/membership/custom_field_validation_test.exs @@ -1,7 +1,7 @@ defmodule Mv.Membership.CustomFieldValidationTest do @moduledoc """ Tests for CustomField validation constraints. - + Tests cover: - Name length validation (max 100 characters) - Name trimming @@ -203,4 +203,3 @@ defmodule Mv.Membership.CustomFieldValidationTest do end end end - diff --git a/test/membership/custom_field_value_validation_test.exs b/test/membership/custom_field_value_validation_test.exs index ce5b5c6..dd3438a 100644 --- a/test/membership/custom_field_value_validation_test.exs +++ b/test/membership/custom_field_value_validation_test.exs @@ -1,7 +1,7 @@ defmodule Mv.Membership.CustomFieldValueValidationTest do @moduledoc """ Tests for CustomFieldValue validation constraints. - + Tests cover: - String value length validation (max 10,000 characters) - String value trimming @@ -184,6 +184,36 @@ defmodule Mv.Membership.CustomFieldValueValidationTest do end describe "email value validation" do + test "accepts nil value (optional field)", %{member: member, email_field: email_field} do + assert {:ok, custom_field_value} = + CustomFieldValue + |> Ash.Changeset.for_create(:create, %{ + member_id: member.id, + custom_field_id: email_field.id, + value: %{"_union_type" => "email", "_union_value" => nil} + }) + |> Ash.create() + + assert custom_field_value.value.value == nil + end + + test "accepts empty string (becomes nil after trim)", %{ + member: member, + email_field: email_field + } do + assert {:ok, custom_field_value} = + CustomFieldValue + |> Ash.Changeset.for_create(:create, %{ + member_id: member.id, + custom_field_id: email_field.id, + value: %{"_union_type" => "email", "_union_value" => ""} + }) + |> Ash.create() + + # Empty string after trim should become nil + assert custom_field_value.value.value == nil + end + test "accepts valid email", %{member: member, email_field: email_field} do assert {:ok, custom_field_value} = CustomFieldValue @@ -273,4 +303,3 @@ defmodule Mv.Membership.CustomFieldValueValidationTest do end end end -