From aa17240c83812c6f5a0022737a2a38ec22278a1f Mon Sep 17 00:00:00 2001 From: shibao Date: Wed, 9 Feb 2022 23:21:42 -0500 Subject: [PATCH] harden tags context --- lib/cannery/tags.ex | 52 +++++++------- lib/cannery/tags/tag.ex | 14 +++- lib/cannery_web/live/live_helpers.ex | 4 +- .../live/tag_live/form_component.ex | 71 +++++++++---------- lib/cannery_web/live/tag_live/index.ex | 28 ++++---- priv/gettext/actions.pot | 2 +- priv/gettext/default.pot | 6 +- priv/gettext/prompts.pot | 8 +-- 8 files changed, 96 insertions(+), 89 deletions(-) diff --git a/lib/cannery/tags.ex b/lib/cannery/tags.ex index ed606b5..a99ba80 100644 --- a/lib/cannery/tags.ex +++ b/lib/cannery/tags.ex @@ -12,13 +12,12 @@ defmodule Cannery.Tags do ## Examples - iex> list_tags() + iex> list_tags(%User{id: 123}) [%Tag{}, ...] """ - @spec list_tags(User.t() | User.id()) :: [Tag.t()] - def list_tags(%{id: user_id}), do: list_tags(user_id) - def list_tags(user_id), do: Repo.all(from t in Tag, where: t.user_id == ^user_id) + @spec list_tags(User.t()) :: [Tag.t()] + def list_tags(%{id: user_id}), do: Repo.all(from t in Tag, where: t.user_id == ^user_id) @doc """ Gets a single tag. @@ -27,72 +26,77 @@ defmodule Cannery.Tags do ## Examples - iex> get_tag!(123) + iex> get_tag!(123, %User{id: 123}) %Tag{} - iex> get_tag!(456) + iex> get_tag!(456, %User{id: 123}) ** (Ecto.NoResultsError) """ - @spec get_tag!(Tag.id()) :: Tag.t() - def get_tag!(id), do: Repo.get!(Tag, id) + @spec get_tag!(Tag.id(), User.t()) :: Tag.t() + def get_tag!(id, %User{id: user_id}), + do: Repo.one!(from t in Tag, where: t.id == ^id and t.user_id == ^user_id) @doc """ Creates a tag. ## Examples - iex> create_tag(%{field: value}) + iex> create_tag(%{field: value}, %User{id: 123}) {:ok, %Tag{}} - iex> create_tag(%{field: bad_value}) + iex> create_tag(%{field: bad_value}, %User{id: 123}) {:error, %Changeset{}} """ - @spec create_tag(attrs :: map()) :: {:ok, Tag.t()} | {:error, Changeset.t(Tag.new_tag())} - def create_tag(attrs), do: %Tag{} |> Tag.changeset(attrs) |> Repo.insert() + @spec create_tag(attrs :: map(), User.t()) :: + {:ok, Tag.t()} | {:error, Changeset.t(Tag.new_tag())} + def create_tag(attrs, %User{id: user_id}), + do: %Tag{} |> Tag.create_changeset(attrs |> Map.put("user_id", user_id)) |> Repo.insert() @doc """ Updates a tag. ## Examples - iex> update_tag(tag, %{field: new_value}) + iex> update_tag(tag, %{field: new_value}, %User{id: 123}) {:ok, %Tag{}} - iex> update_tag(tag, %{field: bad_value}) + iex> update_tag(tag, %{field: bad_value}, %User{id: 123}) {:error, %Changeset{}} """ - @spec update_tag(Tag.t(), attrs :: map()) :: {:ok, Tag.t()} | {:error, Changeset.t(Tag.t())} - def update_tag(tag, attrs), do: tag |> Tag.changeset(attrs) |> Repo.update() + @spec update_tag(Tag.t(), attrs :: map(), User.t()) :: + {:ok, Tag.t()} | {:error, Changeset.t(Tag.t())} + def update_tag(%Tag{user_id: user_id} = tag, attrs, %User{id: user_id}), + do: tag |> Tag.update_changeset(attrs) |> Repo.update() @doc """ Deletes a tag. ## Examples - iex> delete_tag(tag) + iex> delete_tag(tag, %User{id: 123}) {:ok, %Tag{}} - iex> delete_tag(tag) + iex> delete_tag(tag, %User{id: 123}) {:error, %Changeset{}} """ - @spec delete_tag(Tag.t()) :: {:ok, Tag.t()} | {:error, Changeset.t(Tag.t())} - def delete_tag(tag), do: tag |> Repo.delete() + @spec delete_tag(Tag.t(), User.t()) :: {:ok, Tag.t()} | {:error, Changeset.t(Tag.t())} + def delete_tag(%Tag{user_id: user_id} = tag, %User{id: user_id}), do: tag |> Repo.delete() @doc """ Deletes a tag. ## Examples - iex> delete_tag!(tag) + iex> delete_tag!(tag, %User{id: 123}) %Tag{} """ - @spec delete_tag!(Tag.t()) :: Tag.t() - def delete_tag!(tag), do: tag |> Repo.delete!() + @spec delete_tag!(Tag.t(), User.t()) :: Tag.t() + def delete_tag!(%Tag{user_id: user_id} = tag, %User{id: user_id}), do: tag |> Repo.delete!() @doc """ Returns an `%Changeset{}` for tracking tag changes. @@ -106,7 +110,7 @@ defmodule Cannery.Tags do @spec change_tag(Tag.t() | Tag.new_tag()) :: Changeset.t(Tag.t() | Tag.new_tag()) @spec change_tag(Tag.t() | Tag.new_tag(), attrs :: map()) :: Changeset.t(Tag.t() | Tag.new_tag()) - def change_tag(tag, attrs \\ %{}), do: Tag.changeset(tag, attrs) + def change_tag(tag, attrs \\ %{}), do: Tag.update_changeset(tag, attrs) @doc """ Get a random tag bg_color in `#ffffff` hex format diff --git a/lib/cannery/tags/tag.ex b/lib/cannery/tags/tag.ex index 2f74436..287a771 100644 --- a/lib/cannery/tags/tag.ex +++ b/lib/cannery/tags/tag.ex @@ -26,7 +26,7 @@ defmodule Cannery.Tags.Tag do name: String.t(), bg_color: String.t(), text_color: String.t(), - user: User.t(), + user: User.t() | nil, user_id: User.id(), inserted_at: NaiveDateTime.t(), updated_at: NaiveDateTime.t() @@ -35,10 +35,18 @@ defmodule Cannery.Tags.Tag do @type id() :: UUID.t() @doc false - @spec changeset(t() | new_tag(), attrs :: map()) :: Changeset.t(t() | new_tag()) - def changeset(tag, attrs) do + @spec create_changeset(t() | new_tag(), attrs :: map()) :: Changeset.t(t() | new_tag()) + def create_changeset(tag, attrs) do tag |> cast(attrs, [:name, :bg_color, :text_color, :user_id]) |> validate_required([:name, :bg_color, :text_color, :user_id]) end + + @doc false + @spec update_changeset(t() | new_tag(), attrs :: map()) :: Changeset.t(t() | new_tag()) + def update_changeset(tag, attrs) do + tag + |> cast(attrs, [:name, :bg_color, :text_color]) + |> validate_required([:name, :bg_color, :text_color, :user_id]) + end end diff --git a/lib/cannery_web/live/live_helpers.ex b/lib/cannery_web/live/live_helpers.ex index ba05d0f..a390c8b 100644 --- a/lib/cannery_web/live/live_helpers.ex +++ b/lib/cannery_web/live/live_helpers.ex @@ -29,9 +29,7 @@ defmodule CanneryWeb.LiveHelpers do def assign_defaults(socket, %{"user_token" => user_token} = _session) do socket - |> assign_new(:current_user, fn -> - Accounts.get_user_by_session_token(user_token) - end) + |> assign_new(:current_user, fn -> Accounts.get_user_by_session_token(user_token) end) end def assign_defaults(socket, _session) do diff --git a/lib/cannery_web/live/tag_live/form_component.ex b/lib/cannery_web/live/tag_live/form_component.ex index 4035eb3..12f3290 100644 --- a/lib/cannery_web/live/tag_live/form_component.ex +++ b/lib/cannery_web/live/tag_live/form_component.ex @@ -4,29 +4,21 @@ defmodule CanneryWeb.TagLive.FormComponent do """ use CanneryWeb, :live_component - alias Cannery.Tags alias Ecto.Changeset @impl true def update(%{tag: tag} = assigns, socket) do - changeset = Tags.change_tag(tag) - - {:ok, - socket - |> assign(assigns) - |> assign(:changeset, changeset)} + {:ok, socket |> assign(assigns) |> assign(:changeset, Tags.change_tag(tag))} end @impl true - def handle_event("validate", %{"tag" => tag_params}, socket) do - tag_params = tag_params |> Map.put("user_id", socket.assigns.current_user.id) - changeset = socket.assigns.tag |> Tags.change_tag(tag_params) - {:noreply, socket |> assign(:changeset, changeset)} + def handle_event("validate", %{"tag" => tag_params}, %{assigns: %{tag: tag}} = socket) do + {:noreply, socket |> assign(:changeset, tag |> Tags.change_tag(tag_params))} end - def handle_event("save", %{"tag" => tag_params}, socket) do - save_tag(socket, socket.assigns.action, tag_params) + def handle_event("save", %{"tag" => tag_params}, %{assigns: %{action: action}} = socket) do + save_tag(socket, action, tag_params) end @impl true @@ -76,32 +68,39 @@ defmodule CanneryWeb.TagLive.FormComponent do """ end - defp save_tag(socket, :edit, tag_params) do - case Tags.update_tag(socket.assigns.tag, tag_params) do - {:ok, _tag} -> - {:noreply, - socket - |> put_flash(:info, dgettext("prompts", "Tag updated successfully")) - |> push_redirect(to: socket.assigns.return_to)} + defp save_tag( + %{assigns: %{tag: tag, current_user: current_user, return_to: return_to}} = socket, + :edit, + tag_params + ) do + socket = + case Tags.update_tag(tag, tag_params, current_user) do + {:ok, %{name: tag_name}} -> + prompt = dgettext("prompts", "%{name} updated successfully", name: tag_name) + socket |> put_flash(:info, prompt) |> push_redirect(to: return_to) - {:error, %Changeset{} = changeset} -> - {:noreply, socket |> assign(:changeset, changeset)} - end + {:error, %Changeset{} = changeset} -> + socket |> assign(:changeset, changeset) + end + + {:noreply, socket} end - defp save_tag(socket, :new, tag_params) do - tag_params - |> Map.put("user_id", socket.assigns.current_user.id) - |> Tags.create_tag() - |> case do - {:ok, _tag} -> - {:noreply, - socket - |> put_flash(:info, dgettext("prompts", "Tag created successfully")) - |> push_redirect(to: socket.assigns.return_to)} + defp save_tag( + %{assigns: %{current_user: current_user, return_to: return_to}} = socket, + :new, + tag_params + ) do + socket = + case Tags.create_tag(tag_params, current_user) do + {:ok, %{name: tag_name}} -> + prompt = dgettext("prompts", "%{name} created successfully", name: tag_name) + socket |> put_flash(:info, prompt) |> push_redirect(to: return_to) - {:error, %Changeset{} = changeset} -> - {:noreply, socket |> assign(changeset: changeset)} - end + {:error, %Changeset{} = changeset} -> + socket |> assign(changeset: changeset) + end + + {:noreply, socket} end end diff --git a/lib/cannery_web/live/tag_live/index.ex b/lib/cannery_web/live/tag_live/index.ex index 642e058..4e2bac4 100644 --- a/lib/cannery_web/live/tag_live/index.ex +++ b/lib/cannery_web/live/tag_live/index.ex @@ -14,14 +14,14 @@ defmodule CanneryWeb.TagLive.Index do end @impl true - def handle_params(params, _url, socket) do - {:noreply, apply_action(socket, socket.assigns.live_action, params)} + def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do + {:noreply, apply_action(socket, live_action, params)} end - defp apply_action(socket, :edit, %{"id" => id}) do + defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do socket |> assign(:page_title, gettext("Edit Tag")) - |> assign(:tag, Tags.get_tag!(id)) + |> assign(:tag, Tags.get_tag!(id, current_user)) end defp apply_action(socket, :new, _params) do @@ -31,21 +31,19 @@ defmodule CanneryWeb.TagLive.Index do end defp apply_action(socket, :index, _params) do - socket - |> assign(:page_title, gettext("Listing Tags")) - |> assign(:tag, nil) + socket |> assign(:page_title, gettext("Listing Tags")) |> assign(:tag, nil) end @impl true - def handle_event("delete", %{"id" => id}, socket) do - tag = Tags.get_tag!(id) - {:ok, _} = Tags.delete_tag(tag) - socket = socket |> put_flash(:info, dgettext("prompts", "Tag deleted succesfully")) - {:noreply, socket |> display_tags()} + def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do + %{name: tag_name} = Tags.get_tag!(id, current_user) |> Tags.delete_tag!(current_user) + + prompt = dgettext("prompts", "%{name} deleted succesfully", name: tag_name) + + {:noreply, socket |> put_flash(:info, prompt) |> display_tags()} end - defp display_tags(socket) do - tags = Tags.list_tags(socket.assigns.current_user) - socket |> assign(tags: tags) + defp display_tags(%{assigns: %{current_user: current_user}} = socket) do + socket |> assign(tags: Tags.list_tags(current_user)) end end diff --git a/priv/gettext/actions.pot b/priv/gettext/actions.pot index 362807b..641f5b8 100644 --- a/priv/gettext/actions.pot +++ b/priv/gettext/actions.pot @@ -95,7 +95,7 @@ msgstr "" #: lib/cannery_web/live/ammo_type_live/form_component.ex:155 #: lib/cannery_web/live/container_live/form_component.ex:79 #: lib/cannery_web/live/invite_live/form_component.ex:61 -#: lib/cannery_web/live/tag_live/form_component.ex:70 +#: lib/cannery_web/live/tag_live/form_component.ex:69 msgid "Save" msgstr "" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index e1e6516..8199394 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -305,7 +305,7 @@ msgstr "" #: lib/cannery_web/live/ammo_type_live/index.html.heex:26 #: lib/cannery_web/live/container_live/form_component.ex:49 #: lib/cannery_web/live/invite_live/form_component.ex:53 -#: lib/cannery_web/live/tag_live/form_component.ex:54 +#: lib/cannery_web/live/tag_live/form_component.ex:53 msgid "Name" msgstr "" @@ -451,7 +451,7 @@ msgid "Type:" msgstr "" #, elixir-format, ex-autogen -#: lib/cannery_web/live/tag_live/form_component.ex:58 +#: lib/cannery_web/live/tag_live/form_component.ex:57 msgid "Background color" msgstr "" @@ -523,7 +523,7 @@ msgid "Tags can be added to your containers to help you organize" msgstr "" #, elixir-format, ex-autogen -#: lib/cannery_web/live/tag_live/form_component.ex:64 +#: lib/cannery_web/live/tag_live/form_component.ex:63 msgid "Text color" msgstr "" diff --git a/priv/gettext/prompts.pot b/priv/gettext/prompts.pot index fbb8770..0296bb1 100644 --- a/priv/gettext/prompts.pot +++ b/priv/gettext/prompts.pot @@ -98,7 +98,7 @@ msgstr "" #: lib/cannery_web/live/ammo_type_live/form_component.ex:156 #: lib/cannery_web/live/container_live/form_component.ex:81 #: lib/cannery_web/live/invite_live/form_component.ex:63 -#: lib/cannery_web/live/tag_live/form_component.ex:72 +#: lib/cannery_web/live/tag_live/form_component.ex:71 msgid "Saving..." msgstr "" @@ -152,16 +152,16 @@ msgid "Invite updated successfully" msgstr "" #, elixir-format, ex-autogen -#: lib/cannery_web/live/tag_live/form_component.ex:100 +#: lib/cannery_web/live/tag_live/form_component.ex:98 msgid "Tag created successfully" msgstr "" #, elixir-format, ex-autogen -#: lib/cannery_web/live/tag_live/index.ex:43 +#: lib/cannery_web/live/tag_live/index.ex:45 msgid "Tag deleted succesfully" msgstr "" #, elixir-format, ex-autogen -#: lib/cannery_web/live/tag_live/form_component.ex:84 +#: lib/cannery_web/live/tag_live/form_component.ex:83 msgid "Tag updated successfully" msgstr ""