From 264f13e523f35e5c1037923449df0b91629006b6 Mon Sep 17 00:00:00 2001 From: shibao Date: Sat, 26 Nov 2022 14:51:18 -0500 Subject: [PATCH] use slugs --- assets/js/app.js | 4 +- lib/memex/contexts.ex | 39 +++++++- lib/memex/contexts/context.ex | 21 ++-- lib/memex/notes.ex | 41 +++++++- lib/memex/notes/note.ex | 20 ++-- lib/memex/pipelines.ex | 41 +++++++- lib/memex/pipelines/pipeline.ex | 20 ++-- .../components/contexts_table_component.ex | 16 ++-- .../components/notes_table_component.ex | 16 ++-- .../components/pipelines_table_component.ex | 16 ++-- .../live/context_live/form_component.ex | 8 +- .../context_live/form_component.html.heex | 6 +- lib/memex_web/live/context_live/index.ex | 10 +- .../live/context_live/index.html.heex | 2 +- lib/memex_web/live/context_live/show.ex | 16 ++-- .../live/context_live/show.html.heex | 11 ++- .../live/note_live/form_component.ex | 8 +- .../live/note_live/form_component.html.heex | 6 +- lib/memex_web/live/note_live/index.ex | 10 +- lib/memex_web/live/note_live/index.html.heex | 2 +- lib/memex_web/live/note_live/show.ex | 16 ++-- lib/memex_web/live/note_live/show.html.heex | 8 +- .../live/pipeline_live/form_component.ex | 8 +- .../pipeline_live/form_component.html.heex | 6 +- lib/memex_web/live/pipeline_live/index.ex | 10 +- .../live/pipeline_live/index.html.heex | 2 +- lib/memex_web/live/pipeline_live/show.ex | 16 ++-- .../live/pipeline_live/show.html.heex | 11 ++- lib/memex_web/not_found_error.ex | 3 + lib/memex_web/router.ex | 18 ++-- lib/memex_web/templates/error/error.html.heex | 4 +- lib/memex_web/views/error_view.ex | 6 +- priv/gettext/actions.pot | 8 +- priv/gettext/default.pot | 95 ++++++++++--------- priv/gettext/errors.pot | 50 ++++++---- priv/gettext/prompts.pot | 4 +- .../migrations/20221126182210_use_slugs.exs | 14 +++ test/memex/contexts_test.exs | 41 ++++++-- test/memex/notes_test.exs | 41 ++++++-- test/memex/pipelines_test.exs | 41 ++++++-- test/memex_web/live/context_live_test.exs | 26 ++--- test/memex_web/live/note_live_test.exs | 26 ++--- test/memex_web/live/pipeline_live_test.exs | 26 ++--- test/memex_web/views/error_view_test.exs | 4 +- test/support/fixtures.ex | 10 +- test/support/fixtures/contexts_fixtures.ex | 3 +- test/support/fixtures/notes_fixtures.ex | 3 +- test/support/fixtures/pipelines_fixtures.ex | 3 +- 48 files changed, 536 insertions(+), 280 deletions(-) create mode 100644 lib/memex_web/not_found_error.ex create mode 100644 priv/repo/migrations/20221126182210_use_slugs.exs diff --git a/assets/js/app.js b/assets/js/app.js index 1c8f365..8b98397 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -29,7 +29,9 @@ import topbar from '../vendor/topbar' import MaintainAttrs from './maintain_attrs' import Alpine from 'alpinejs' -const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute('content') +const csrfTokenElement = document.querySelector("meta[name='csrf-token']") +let csrfToken +if (csrfTokenElement) { csrfToken = csrfTokenElement.getAttribute('content') } const liveSocket = new LiveSocket('/live', Socket, { dom: { onBeforeElUpdated (from, to) { diff --git a/lib/memex/contexts.ex b/lib/memex/contexts.ex index f173d29..fdf1062 100644 --- a/lib/memex/contexts.ex +++ b/lib/memex/contexts.ex @@ -16,7 +16,7 @@ defmodule Memex.Contexts do [%Context{}, ...] iex> list_contexts("my context", %User{id: 123}) - [%Context{title: "my context"}, ...] + [%Context{slug: "my context"}, ...] """ @spec list_contexts(User.t()) :: [Context.t()] @@ -24,7 +24,7 @@ defmodule Memex.Contexts do def list_contexts(search \\ nil, user) def list_contexts(search, %{id: user_id}) when search |> is_nil() or search == "" do - Repo.all(from c in Context, where: c.user_id == ^user_id, order_by: c.title) + Repo.all(from c in Context, where: c.user_id == ^user_id, order_by: c.slug) end def list_contexts(search, %{id: user_id}) when search |> is_binary() do @@ -57,7 +57,7 @@ defmodule Memex.Contexts do [%Context{}, ...] iex> list_public_contexts("my context") - [%Context{title: "my context"}, ...] + [%Context{slug: "my context"}, ...] """ @spec list_public_contexts() :: [Context.t()] @@ -65,7 +65,7 @@ defmodule Memex.Contexts do def list_public_contexts(search \\ nil) def list_public_contexts(search) when search |> is_nil() or search == "" do - Repo.all(from c in Context, where: c.visibility == :public, order_by: c.title) + Repo.all(from c in Context, where: c.visibility == :public, order_by: c.slug) end def list_public_contexts(search) when search |> is_binary() do @@ -120,6 +120,37 @@ defmodule Memex.Contexts do ) end + @doc """ + Gets a single context by a slug. + + Raises `Ecto.NoResultsError` if the Context does not exist. + + ## Examples + + iex> get_context_by_slug("my-context", %User{id: 123}) + %Context{} + + iex> get_context_by_slug("my-context", %User{id: 123}) + ** (Ecto.NoResultsError) + + """ + @spec get_context_by_slug(Context.slug(), User.t()) :: Context.t() | nil + def get_context_by_slug(slug, %{id: user_id}) do + Repo.one( + from c in Context, + where: c.slug == ^slug, + where: c.user_id == ^user_id or c.visibility in [:public, :unlisted] + ) + end + + def get_context_by_slug(slug, _invalid_user) do + Repo.one( + from c in Context, + where: c.slug == ^slug, + where: c.visibility in [:public, :unlisted] + ) + end + @doc """ Creates a context. diff --git a/lib/memex/contexts/context.ex b/lib/memex/contexts/context.ex index d8e0801..0c895d2 100644 --- a/lib/memex/contexts/context.ex +++ b/lib/memex/contexts/context.ex @@ -3,19 +3,19 @@ defmodule Memex.Contexts.Context do Represents a document that synthesizes multiple concepts as defined by notes into a single consideration """ - use Ecto.Schema import Ecto.Changeset + import MemexWeb.Gettext alias Ecto.{Changeset, UUID} alias Memex.Accounts.User @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "contexts" do + field :slug, :string field :content, :string field :tags, {:array, :string} field :tags_string, :string, virtual: true - field :title, :string field :visibility, Ecto.Enum, values: [:public, :private, :unlisted] belongs_to :user, User @@ -24,7 +24,7 @@ defmodule Memex.Contexts.Context do end @type t :: %__MODULE__{ - title: String.t(), + slug: slug(), content: String.t(), tags: [String.t()] | nil, tags_string: String.t(), @@ -35,24 +35,31 @@ defmodule Memex.Contexts.Context do updated_at: NaiveDateTime.t() } @type id :: UUID.t() + @type slug :: String.t() @type changeset :: Changeset.t(t()) @doc false @spec create_changeset(attrs :: map(), User.t()) :: changeset() def create_changeset(attrs, %User{id: user_id}) do %__MODULE__{} - |> cast(attrs, [:title, :content, :tags, :visibility]) + |> cast(attrs, [:slug, :content, :tags, :visibility]) |> change(user_id: user_id) |> cast_tags_string(attrs) - |> validate_required([:title, :content, :user_id, :visibility]) + |> validate_format(:slug, ~r/^[\p{L}\p{N}\-]+$/, + message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") + ) + |> validate_required([:slug, :content, :user_id, :visibility]) end @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() def update_changeset(%{user_id: user_id} = note, attrs, %User{id: user_id}) do note - |> cast(attrs, [:title, :content, :tags, :visibility]) + |> cast(attrs, [:slug, :content, :tags, :visibility]) |> cast_tags_string(attrs) - |> validate_required([:title, :content, :visibility]) + |> validate_format(:slug, ~r/^[\p{L}\p{N}\-]+$/, + message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") + ) + |> validate_required([:slug, :content, :visibility]) end defp cast_tags_string(changeset, %{"tags_string" => tags_string}) diff --git a/lib/memex/notes.ex b/lib/memex/notes.ex index ef04f80..34b2ea5 100644 --- a/lib/memex/notes.ex +++ b/lib/memex/notes.ex @@ -16,7 +16,7 @@ defmodule Memex.Notes do [%Note{}, ...] iex> list_notes("my note", %User{id: 123}) - [%Note{title: "my note"}, ...] + [%Note{slug: "my note"}, ...] """ @spec list_notes(User.t()) :: [Note.t()] @@ -24,7 +24,7 @@ defmodule Memex.Notes do def list_notes(search \\ nil, user) def list_notes(search, %{id: user_id}) when search |> is_nil() or search == "" do - Repo.all(from n in Note, where: n.user_id == ^user_id, order_by: n.title) + Repo.all(from n in Note, where: n.user_id == ^user_id, order_by: n.slug) end def list_notes(search, %{id: user_id}) when search |> is_binary() do @@ -57,14 +57,14 @@ defmodule Memex.Notes do [%Note{}, ...] iex> list_public_notes("my note") - [%Note{title: "my note"}, ...] + [%Note{slug: "my note"}, ...] """ @spec list_public_notes() :: [Note.t()] @spec list_public_notes(search :: String.t() | nil) :: [Note.t()] def list_public_notes(search \\ nil) def list_public_notes(search) when search |> is_nil() or search == "" do - Repo.all(from n in Note, where: n.visibility == :public, order_by: n.title) + Repo.all(from n in Note, where: n.visibility == :public, order_by: n.slug) end def list_public_notes(search) when search |> is_binary() do @@ -119,6 +119,37 @@ defmodule Memex.Notes do ) end + @doc """ + Gets a single note by slug. + + Raises `Ecto.NoResultsError` if the Note does not exist. + + ## Examples + + iex> get_note_by_slug("my-note", %User{id: 123}) + %Note{} + + iex> get_note_by_slug("my-note", %User{id: 123}) + ** (Ecto.NoResultsError) + + """ + @spec get_note_by_slug(Note.slug(), User.t()) :: Note.t() | nil + def get_note_by_slug(slug, %{id: user_id}) do + Repo.one( + from n in Note, + where: n.slug == ^slug, + where: n.user_id == ^user_id or n.visibility in [:public, :unlisted] + ) + end + + def get_note_by_slug(slug, _invalid_user) do + Repo.one( + from n in Note, + where: n.slug == ^slug, + where: n.visibility in [:public, :unlisted] + ) + end + @doc """ Creates a note. @@ -189,7 +220,7 @@ defmodule Memex.Notes do iex> change_note(note, %User{id: 123}) %Ecto.Changeset{data: %Note{}} - iex> change_note(note, %{title: "new title"}, %User{id: 123}) + iex> change_note(note, %{slug: "new slug"}, %User{id: 123}) %Ecto.Changeset{data: %Note{}} """ diff --git a/lib/memex/notes/note.ex b/lib/memex/notes/note.ex index 641c8a2..e6ec37c 100644 --- a/lib/memex/notes/note.ex +++ b/lib/memex/notes/note.ex @@ -4,13 +4,14 @@ defmodule Memex.Notes.Note do """ use Ecto.Schema import Ecto.Changeset + import MemexWeb.Gettext alias Ecto.{Changeset, UUID} alias Memex.Accounts.User @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "notes" do - field :title, :string + field :slug, :string field :content, :string field :tags, {:array, :string} field :tags_string, :string, virtual: true @@ -22,7 +23,7 @@ defmodule Memex.Notes.Note do end @type t :: %__MODULE__{ - title: String.t(), + slug: slug(), content: String.t(), tags: [String.t()] | nil, tags_string: String.t(), @@ -33,24 +34,31 @@ defmodule Memex.Notes.Note do updated_at: NaiveDateTime.t() } @type id :: UUID.t() + @type slug :: String.t() @type changeset :: Changeset.t(t()) @doc false @spec create_changeset(attrs :: map(), User.t()) :: changeset() def create_changeset(attrs, %User{id: user_id}) do %__MODULE__{} - |> cast(attrs, [:title, :content, :tags, :visibility]) + |> cast(attrs, [:slug, :content, :tags, :visibility]) |> change(user_id: user_id) |> cast_tags_string(attrs) - |> validate_required([:title, :content, :user_id, :visibility]) + |> validate_format(:slug, ~r/^[\p{L}\p{N}\-]+$/, + message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") + ) + |> validate_required([:slug, :content, :user_id, :visibility]) end @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() def update_changeset(%{user_id: user_id} = note, attrs, %User{id: user_id}) do note - |> cast(attrs, [:title, :content, :tags, :visibility]) + |> cast(attrs, [:slug, :content, :tags, :visibility]) |> cast_tags_string(attrs) - |> validate_required([:title, :content, :visibility]) + |> validate_format(:slug, ~r/^[\p{L}\p{N}\-]+$/, + message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") + ) + |> validate_required([:slug, :content, :visibility]) end defp cast_tags_string(changeset, %{"tags_string" => tags_string}) diff --git a/lib/memex/pipelines.ex b/lib/memex/pipelines.ex index 995f3ba..09384d7 100644 --- a/lib/memex/pipelines.ex +++ b/lib/memex/pipelines.ex @@ -16,7 +16,7 @@ defmodule Memex.Pipelines do [%Pipeline{}, ...] iex> list_pipelines("my pipeline", %User{id: 123}) - [%Pipeline{title: "my pipeline"}, ...] + [%Pipeline{slug: "my pipeline"}, ...] """ @spec list_pipelines(User.t()) :: [Pipeline.t()] @@ -24,7 +24,7 @@ defmodule Memex.Pipelines do def list_pipelines(search \\ nil, user) def list_pipelines(search, %{id: user_id}) when search |> is_nil() or search == "" do - Repo.all(from p in Pipeline, where: p.user_id == ^user_id, order_by: p.title) + Repo.all(from p in Pipeline, where: p.user_id == ^user_id, order_by: p.slug) end def list_pipelines(search, %{id: user_id}) when search |> is_binary() do @@ -57,14 +57,14 @@ defmodule Memex.Pipelines do [%Pipeline{}, ...] iex> list_public_pipelines("my pipeline") - [%Pipeline{title: "my pipeline"}, ...] + [%Pipeline{slug: "my pipeline"}, ...] """ @spec list_public_pipelines() :: [Pipeline.t()] @spec list_public_pipelines(search :: String.t() | nil) :: [Pipeline.t()] def list_public_pipelines(search \\ nil) def list_public_pipelines(search) when search |> is_nil() or search == "" do - Repo.all(from p in Pipeline, where: p.visibility == :public, order_by: p.title) + Repo.all(from p in Pipeline, where: p.visibility == :public, order_by: p.slug) end def list_public_pipelines(search) when search |> is_binary() do @@ -119,6 +119,37 @@ defmodule Memex.Pipelines do ) end + @doc """ + Gets a single pipeline by it's slug. + + Raises `Ecto.NoResultsError` if the Pipeline does not exist. + + ## Examples + + iex> get_pipeline_by_slug("my-pipeline", %User{id: 123}) + %Pipeline{} + + iex> get_pipeline_by_slug("my-pipeline", %User{id: 123}) + ** (Ecto.NoResultsError) + + """ + @spec get_pipeline_by_slug(Pipeline.slug(), User.t()) :: Pipeline.t() | nil + def get_pipeline_by_slug(slug, %{id: user_id}) do + Repo.one( + from p in Pipeline, + where: p.slug == ^slug, + where: p.user_id == ^user_id or p.visibility in [:public, :unlisted] + ) + end + + def get_pipeline_by_slug(slug, _invalid_user) do + Repo.one( + from p in Pipeline, + where: p.slug == ^slug, + where: p.visibility in [:public, :unlisted] + ) + end + @doc """ Creates a pipeline. @@ -191,7 +222,7 @@ defmodule Memex.Pipelines do iex> change_pipeline(pipeline, %User{id: 123}) %Ecto.Changeset{data: %Pipeline{}} - iex> change_pipeline(pipeline, %{title: "new title"}, %User{id: 123}) + iex> change_pipeline(pipeline, %{slug: "new slug"}, %User{id: 123}) %Ecto.Changeset{data: %Pipeline{}} """ diff --git a/lib/memex/pipelines/pipeline.ex b/lib/memex/pipelines/pipeline.ex index 6ef0505..17b0d71 100644 --- a/lib/memex/pipelines/pipeline.ex +++ b/lib/memex/pipelines/pipeline.ex @@ -4,13 +4,14 @@ defmodule Memex.Pipelines.Pipeline do """ use Ecto.Schema import Ecto.Changeset + import MemexWeb.Gettext alias Ecto.{Changeset, UUID} alias Memex.{Accounts.User, Pipelines.Step} @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "pipelines" do - field :title, :string + field :slug, :string field :description, :string field :tags, {:array, :string} field :tags_string, :string, virtual: true @@ -24,7 +25,7 @@ defmodule Memex.Pipelines.Pipeline do end @type t :: %__MODULE__{ - title: String.t(), + slug: slug(), description: String.t(), tags: [String.t()] | nil, tags_string: String.t(), @@ -35,24 +36,31 @@ defmodule Memex.Pipelines.Pipeline do updated_at: NaiveDateTime.t() } @type id :: UUID.t() + @type slug :: String.t() @type changeset :: Changeset.t(t()) @doc false @spec create_changeset(attrs :: map(), User.t()) :: changeset() def create_changeset(attrs, %User{id: user_id}) do %__MODULE__{} - |> cast(attrs, [:title, :description, :tags, :visibility]) + |> cast(attrs, [:slug, :description, :tags, :visibility]) |> change(user_id: user_id) |> cast_tags_string(attrs) - |> validate_required([:title, :user_id, :visibility]) + |> validate_format(:slug, ~r/^[\p{L}\p{N}\-]+$/, + message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") + ) + |> validate_required([:slug, :user_id, :visibility]) end @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() def update_changeset(%{user_id: user_id} = pipeline, attrs, %User{id: user_id}) do pipeline - |> cast(attrs, [:title, :description, :tags, :visibility]) + |> cast(attrs, [:slug, :description, :tags, :visibility]) |> cast_tags_string(attrs) - |> validate_required([:title, :visibility]) + |> validate_format(:slug, ~r/^[\p{L}\p{N}\-]+$/, + message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") + ) + |> validate_required([:slug, :visibility]) end defp cast_tags_string(changeset, %{"tags_string" => tags_string}) diff --git a/lib/memex_web/components/contexts_table_component.ex b/lib/memex_web/components/contexts_table_component.ex index 30b6ae3..1443366 100644 --- a/lib/memex_web/components/contexts_table_component.ex +++ b/lib/memex_web/components/contexts_table_component.ex @@ -44,7 +44,7 @@ defmodule MemexWeb.Components.ContextsTableComponent do end columns = [ - %{label: gettext("title"), key: :title}, + %{label: gettext("slug"), key: :slug}, %{label: gettext("content"), key: :content}, %{label: gettext("tags"), key: :tags}, %{label: gettext("visibility"), key: :visibility} @@ -89,20 +89,20 @@ defmodule MemexWeb.Components.ContextsTableComponent do @spec get_value_for_key(atom(), Context.t(), additional_data :: map()) :: any() | {any(), Rendered.t()} - defp get_value_for_key(:title, %{id: id, title: title}, _additional_data) do - assigns = %{id: id, title: title} + defp get_value_for_key(:slug, %{slug: slug}, _additional_data) do + assigns = %{slug: slug} - title_block = ~H""" + slug_block = ~H""" <.link - navigate={Routes.context_show_path(Endpoint, :show, @id)} + navigate={Routes.context_show_path(Endpoint, :show, @slug)} class="link" - data-qa={"context-show-#{@id}"} + data-qa={"context-show-#{@slug}"} > - <%= @title %> + <%= @slug %> """ - {title, title_block} + {slug, slug_block} end defp get_value_for_key(:content, %{content: content}, _additional_data) do diff --git a/lib/memex_web/components/notes_table_component.ex b/lib/memex_web/components/notes_table_component.ex index 21a595d..081f981 100644 --- a/lib/memex_web/components/notes_table_component.ex +++ b/lib/memex_web/components/notes_table_component.ex @@ -44,7 +44,7 @@ defmodule MemexWeb.Components.NotesTableComponent do end columns = [ - %{label: gettext("title"), key: :title}, + %{label: gettext("slug"), key: :slug}, %{label: gettext("content"), key: :content}, %{label: gettext("tags"), key: :tags}, %{label: gettext("visibility"), key: :visibility} @@ -89,20 +89,20 @@ defmodule MemexWeb.Components.NotesTableComponent do @spec get_value_for_key(atom(), Note.t(), additional_data :: map()) :: any() | {any(), Rendered.t()} - defp get_value_for_key(:title, %{id: id, title: title}, _additional_data) do - assigns = %{id: id, title: title} + defp get_value_for_key(:slug, %{slug: slug}, _additional_data) do + assigns = %{slug: slug} - title_block = ~H""" + slug_block = ~H""" <.link - navigate={Routes.note_show_path(Endpoint, :show, @id)} + navigate={Routes.note_show_path(Endpoint, :show, @slug)} class="link" - data-qa={"note-show-#{@id}"} + data-qa={"note-show-#{@slug}"} > - <%= @title %> + <%= @slug %> """ - {title, title_block} + {slug, slug_block} end defp get_value_for_key(:content, %{content: content}, _additional_data) do diff --git a/lib/memex_web/components/pipelines_table_component.ex b/lib/memex_web/components/pipelines_table_component.ex index 9f49a6d..f3a9afd 100644 --- a/lib/memex_web/components/pipelines_table_component.ex +++ b/lib/memex_web/components/pipelines_table_component.ex @@ -44,7 +44,7 @@ defmodule MemexWeb.Components.PipelinesTableComponent do end columns = [ - %{label: gettext("title"), key: :title}, + %{label: gettext("slug"), key: :slug}, %{label: gettext("description"), key: :description}, %{label: gettext("tags"), key: :tags}, %{label: gettext("visibility"), key: :visibility} @@ -89,20 +89,20 @@ defmodule MemexWeb.Components.PipelinesTableComponent do @spec get_value_for_key(atom(), Pipeline.t(), additional_data :: map()) :: any() | {any(), Rendered.t()} - defp get_value_for_key(:title, %{id: id, title: title}, _additional_data) do - assigns = %{id: id, title: title} + defp get_value_for_key(:slug, %{slug: slug}, _additional_data) do + assigns = %{slug: slug} - title_block = ~H""" + slug_block = ~H""" <.link - navigate={Routes.pipeline_show_path(Endpoint, :show, @id)} + navigate={Routes.pipeline_show_path(Endpoint, :show, @slug)} class="link" - data-qa={"pipeline-show-#{@id}"} + data-qa={"pipeline-show-#{@slug}"} > - <%= @title %> + <%= @slug %> """ - {title, title_block} + {slug, slug_block} end defp get_value_for_key(:description, %{description: description}, _additional_data) do diff --git a/lib/memex_web/live/context_live/form_component.ex b/lib/memex_web/live/context_live/form_component.ex index 9dec24a..9e188c1 100644 --- a/lib/memex_web/live/context_live/form_component.ex +++ b/lib/memex_web/live/context_live/form_component.ex @@ -38,10 +38,10 @@ defmodule MemexWeb.ContextLive.FormComponent do context_params ) do case Contexts.update_context(context, context_params, current_user) do - {:ok, %{title: title}} -> + {:ok, %{slug: slug}} -> {:noreply, socket - |> put_flash(:info, gettext("%{title} saved", title: title)) + |> put_flash(:info, gettext("%{slug} saved", slug: slug)) |> push_navigate(to: return_to)} {:error, %Ecto.Changeset{} = changeset} -> @@ -55,10 +55,10 @@ defmodule MemexWeb.ContextLive.FormComponent do context_params ) do case Contexts.create_context(context_params, current_user) do - {:ok, %{title: title}} -> + {:ok, %{slug: slug}} -> {:noreply, socket - |> put_flash(:info, gettext("%{title} created", title: title)) + |> put_flash(:info, gettext("%{slug} created", slug: slug)) |> push_navigate(to: return_to)} {:error, %Ecto.Changeset{} = changeset} -> diff --git a/lib/memex_web/live/context_live/form_component.html.heex b/lib/memex_web/live/context_live/form_component.html.heex index e2ba8e9..65c209a 100644 --- a/lib/memex_web/live/context_live/form_component.html.heex +++ b/lib/memex_web/live/context_live/form_component.html.heex @@ -9,11 +9,11 @@ phx-debounce="300" class="flex flex-col justify-start items-stretch space-y-4" > - <%= text_input(f, :title, + <%= text_input(f, :slug, class: "input input-primary", - placeholder: gettext("title") + placeholder: gettext("slug") ) %> - <%= error_tag(f, :title) %> + <%= error_tag(f, :slug) %> <%= textarea(f, :content, id: "context-form-content", diff --git a/lib/memex_web/live/context_live/index.ex b/lib/memex_web/live/context_live/index.ex index e9efdaa..db991d6 100644 --- a/lib/memex_web/live/context_live/index.ex +++ b/lib/memex_web/live/context_live/index.ex @@ -16,11 +16,11 @@ defmodule MemexWeb.ContextLive.Index do {:noreply, apply_action(socket, live_action, params)} end - defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do - %{title: title} = context = Contexts.get_context!(id, current_user) + defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"slug" => slug}) do + %{slug: slug} = context = Contexts.get_context_by_slug(slug, current_user) socket - |> assign(page_title: gettext("edit %{title}", title: title)) + |> assign(page_title: gettext("edit %{slug}", slug: slug)) |> assign(context: context) end @@ -49,12 +49,12 @@ defmodule MemexWeb.ContextLive.Index do @impl true def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do context = Contexts.get_context!(id, current_user) - {:ok, %{title: title}} = Contexts.delete_context(context, current_user) + {:ok, %{slug: slug}} = Contexts.delete_context(context, current_user) socket = socket |> assign(contexts: Contexts.list_contexts(current_user)) - |> put_flash(:info, gettext("%{title} deleted", title: title)) + |> put_flash(:info, gettext("%{slug} deleted", slug: slug)) {:noreply, socket} end diff --git a/lib/memex_web/live/context_live/index.html.heex b/lib/memex_web/live/context_live/index.html.heex index 4e8bdbb..dd0bc8b 100644 --- a/lib/memex_web/live/context_live/index.html.heex +++ b/lib/memex_web/live/context_live/index.html.heex @@ -32,7 +32,7 @@ <:actions :let={context}> <%= if is_owner?(context, @current_user) do %> <.link - patch={Routes.context_index_path(@socket, :edit, context)} + patch={Routes.context_index_path(@socket, :edit, context.slug)} data-qa={"context-edit-#{context.id}"} > <%= dgettext("actions", "edit") %> diff --git a/lib/memex_web/live/context_live/show.ex b/lib/memex_web/live/context_live/show.ex index 66f6940..f478022 100644 --- a/lib/memex_web/live/context_live/show.ex +++ b/lib/memex_web/live/context_live/show.ex @@ -10,11 +10,15 @@ defmodule MemexWeb.ContextLive.Show do @impl true def handle_params( - %{"id" => id}, + %{"slug" => slug}, _, %{assigns: %{live_action: live_action, current_user: current_user}} = socket ) do - context = Contexts.get_context!(id, current_user) + context = + case Contexts.get_context_by_slug(slug, current_user) do + nil -> raise MemexWeb.NotFoundError, gettext("%{slug} could not be found", slug: slug) + context -> context + end socket = socket @@ -30,18 +34,18 @@ defmodule MemexWeb.ContextLive.Show do _params, %{assigns: %{context: context, current_user: current_user}} = socket ) do - {:ok, %{title: title}} = Contexts.delete_context(context, current_user) + {:ok, %{slug: slug}} = Contexts.delete_context(context, current_user) socket = socket - |> put_flash(:info, gettext("%{title} deleted", title: title)) + |> put_flash(:info, gettext("%{slug} deleted", slug: slug)) |> push_navigate(to: Routes.context_index_path(Endpoint, :index)) {:noreply, socket} end - defp page_title(:show, %{title: title}), do: title - defp page_title(:edit, %{title: title}), do: gettext("edit %{title}", title: title) + defp page_title(:show, %{slug: slug}), do: slug + defp page_title(:edit, %{slug: slug}), do: gettext("edit %{slug}", slug: slug) @spec is_owner_or_admin?(Context.t(), User.t()) :: boolean() defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true diff --git a/lib/memex_web/live/context_live/show.html.heex b/lib/memex_web/live/context_live/show.html.heex index 716cd07..ac7000e 100644 --- a/lib/memex_web/live/context_live/show.html.heex +++ b/lib/memex_web/live/context_live/show.html.heex @@ -1,6 +1,6 @@

- <%= @context.title %> + <%= @context.slug %>

<%= if @context.tags, do: @context.tags |> Enum.join(", ") %>

@@ -23,7 +23,10 @@ <%= dgettext("actions", "back") %> <%= if is_owner?(@context, @current_user) do %> - <.link class="btn btn-primary" patch={Routes.context_show_path(@socket, :edit, @context)}> + <.link + class="btn btn-primary" + patch={Routes.context_show_path(@socket, :edit, @context.slug)} + > <%= dgettext("actions", "edit") %> <% end %> @@ -42,7 +45,7 @@
<%= if @live_action in [:edit] do %> - <.modal return_to={Routes.context_show_path(@socket, :show, @context)}> + <.modal return_to={Routes.context_show_path(@socket, :show, @context.slug)}> <.live_component module={MemexWeb.ContextLive.FormComponent} id={@context.id} @@ -50,7 +53,7 @@ title={@page_title} action={@live_action} context={@context} - return_to={Routes.context_show_path(@socket, :show, @context)} + return_to={Routes.context_show_path(@socket, :show, @context.slug)} /> <% end %> diff --git a/lib/memex_web/live/note_live/form_component.ex b/lib/memex_web/live/note_live/form_component.ex index 01833f5..1153927 100644 --- a/lib/memex_web/live/note_live/form_component.ex +++ b/lib/memex_web/live/note_live/form_component.ex @@ -37,10 +37,10 @@ defmodule MemexWeb.NoteLive.FormComponent do note_params ) do case Notes.update_note(note, note_params, current_user) do - {:ok, %{title: title}} -> + {:ok, %{slug: slug}} -> {:noreply, socket - |> put_flash(:info, gettext("%{title} saved", title: title)) + |> put_flash(:info, gettext("%{slug} saved", slug: slug)) |> push_navigate(to: return_to)} {:error, %Ecto.Changeset{} = changeset} -> @@ -54,10 +54,10 @@ defmodule MemexWeb.NoteLive.FormComponent do note_params ) do case Notes.create_note(note_params, current_user) do - {:ok, %{title: title}} -> + {:ok, %{slug: slug}} -> {:noreply, socket - |> put_flash(:info, gettext("%{title} created", title: title)) + |> put_flash(:info, gettext("%{slug} created", slug: slug)) |> push_navigate(to: return_to)} {:error, %Ecto.Changeset{} = changeset} -> diff --git a/lib/memex_web/live/note_live/form_component.html.heex b/lib/memex_web/live/note_live/form_component.html.heex index 75b588d..e2cc50d 100644 --- a/lib/memex_web/live/note_live/form_component.html.heex +++ b/lib/memex_web/live/note_live/form_component.html.heex @@ -9,11 +9,11 @@ phx-debounce="300" class="flex flex-col justify-start items-stretch space-y-4" > - <%= text_input(f, :title, + <%= text_input(f, :slug, class: "input input-primary", - placeholder: gettext("title") + placeholder: gettext("slug") ) %> - <%= error_tag(f, :title) %> + <%= error_tag(f, :slug) %> <%= textarea(f, :content, id: "note-form-content", diff --git a/lib/memex_web/live/note_live/index.ex b/lib/memex_web/live/note_live/index.ex index 18f328c..88e9511 100644 --- a/lib/memex_web/live/note_live/index.ex +++ b/lib/memex_web/live/note_live/index.ex @@ -16,11 +16,11 @@ defmodule MemexWeb.NoteLive.Index do {:noreply, apply_action(socket, live_action, params)} end - defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do - %{title: title} = note = Notes.get_note!(id, current_user) + defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"slug" => slug}) do + %{slug: slug} = note = Notes.get_note_by_slug(slug, current_user) socket - |> assign(page_title: gettext("edit %{title}", title: title)) + |> assign(page_title: gettext("edit %{slug}", slug: slug)) |> assign(note: note) end @@ -49,12 +49,12 @@ defmodule MemexWeb.NoteLive.Index do @impl true def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do note = Notes.get_note!(id, current_user) - {:ok, %{title: title}} = Notes.delete_note(note, current_user) + {:ok, %{slug: slug}} = Notes.delete_note(note, current_user) socket = socket |> assign(notes: Notes.list_notes(current_user)) - |> put_flash(:info, gettext("%{title} deleted", title: title)) + |> put_flash(:info, gettext("%{slug} deleted", slug: slug)) {:noreply, socket} end diff --git a/lib/memex_web/live/note_live/index.html.heex b/lib/memex_web/live/note_live/index.html.heex index 5be724d..f2bf01d 100644 --- a/lib/memex_web/live/note_live/index.html.heex +++ b/lib/memex_web/live/note_live/index.html.heex @@ -32,7 +32,7 @@ <:actions :let={note}> <%= if is_owner?(note, @current_user) do %> <.link - patch={Routes.note_index_path(@socket, :edit, note)} + patch={Routes.note_index_path(@socket, :edit, note.slug)} data-qa={"note-edit-#{note.id}"} > <%= dgettext("actions", "edit") %> diff --git a/lib/memex_web/live/note_live/show.ex b/lib/memex_web/live/note_live/show.ex index de20ce7..65f63b4 100644 --- a/lib/memex_web/live/note_live/show.ex +++ b/lib/memex_web/live/note_live/show.ex @@ -10,11 +10,15 @@ defmodule MemexWeb.NoteLive.Show do @impl true def handle_params( - %{"id" => id}, + %{"slug" => slug}, _, %{assigns: %{live_action: live_action, current_user: current_user}} = socket ) do - note = Notes.get_note!(id, current_user) + note = + case Notes.get_note_by_slug(slug, current_user) do + nil -> raise MemexWeb.NotFoundError, gettext("%{slug} could not be found", slug: slug) + note -> note + end socket = socket @@ -30,18 +34,18 @@ defmodule MemexWeb.NoteLive.Show do _params, %{assigns: %{note: note, current_user: current_user}} = socket ) do - {:ok, %{title: title}} = Notes.delete_note(note, current_user) + {:ok, %{slug: slug}} = Notes.delete_note(note, current_user) socket = socket - |> put_flash(:info, gettext("%{title} deleted", title: title)) + |> put_flash(:info, gettext("%{slug} deleted", slug: slug)) |> push_navigate(to: Routes.note_index_path(Endpoint, :index)) {:noreply, socket} end - defp page_title(:show, %{title: title}), do: title - defp page_title(:edit, %{title: title}), do: gettext("edit %{title}", title: title) + defp page_title(:show, %{slug: slug}), do: slug + defp page_title(:edit, %{slug: slug}), do: gettext("edit %{slug}", slug: slug) @spec is_owner_or_admin?(Note.t(), User.t()) :: boolean() defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true diff --git a/lib/memex_web/live/note_live/show.html.heex b/lib/memex_web/live/note_live/show.html.heex index bca7c55..73026b0 100644 --- a/lib/memex_web/live/note_live/show.html.heex +++ b/lib/memex_web/live/note_live/show.html.heex @@ -1,6 +1,6 @@

- <%= @note.title %> + <%= @note.slug %>

<%= if @note.tags, do: @note.tags |> Enum.join(", ") %>

@@ -23,7 +23,7 @@ <%= dgettext("actions", "back") %> <%= if is_owner?(@note, @current_user) do %> - <.link class="btn btn-primary" patch={Routes.note_show_path(@socket, :edit, @note)}> + <.link class="btn btn-primary" patch={Routes.note_show_path(@socket, :edit, @note.slug)}> <%= dgettext("actions", "edit") %> <% end %> @@ -42,7 +42,7 @@
<%= if @live_action in [:edit] do %> - <.modal return_to={Routes.note_show_path(@socket, :show, @note)}> + <.modal return_to={Routes.note_show_path(@socket, :show, @note.slug)}> <.live_component module={MemexWeb.NoteLive.FormComponent} id={@note.id} @@ -50,7 +50,7 @@ title={@page_title} action={@live_action} note={@note} - return_to={Routes.note_show_path(@socket, :show, @note)} + return_to={Routes.note_show_path(@socket, :show, @note.slug)} /> <% end %> diff --git a/lib/memex_web/live/pipeline_live/form_component.ex b/lib/memex_web/live/pipeline_live/form_component.ex index b55f00d..0479b91 100644 --- a/lib/memex_web/live/pipeline_live/form_component.ex +++ b/lib/memex_web/live/pipeline_live/form_component.ex @@ -42,10 +42,10 @@ defmodule MemexWeb.PipelineLive.FormComponent do pipeline_params ) do case Pipelines.update_pipeline(pipeline, pipeline_params, current_user) do - {:ok, %{title: title}} -> + {:ok, %{slug: slug}} -> {:noreply, socket - |> put_flash(:info, gettext("%{title} saved", title: title)) + |> put_flash(:info, gettext("%{slug} saved", slug: slug)) |> push_navigate(to: return_to)} {:error, %Ecto.Changeset{} = changeset} -> @@ -59,10 +59,10 @@ defmodule MemexWeb.PipelineLive.FormComponent do pipeline_params ) do case Pipelines.create_pipeline(pipeline_params, current_user) do - {:ok, %{title: title}} -> + {:ok, %{slug: slug}} -> {:noreply, socket - |> put_flash(:info, gettext("%{title} created", title: title)) + |> put_flash(:info, gettext("%{slug} created", slug: slug)) |> push_navigate(to: return_to)} {:error, %Ecto.Changeset{} = changeset} -> diff --git a/lib/memex_web/live/pipeline_live/form_component.html.heex b/lib/memex_web/live/pipeline_live/form_component.html.heex index ad1e510..b4b3a65 100644 --- a/lib/memex_web/live/pipeline_live/form_component.html.heex +++ b/lib/memex_web/live/pipeline_live/form_component.html.heex @@ -9,11 +9,11 @@ phx-debounce="300" class="flex flex-col justify-start items-stretch space-y-4" > - <%= text_input(f, :title, + <%= text_input(f, :slug, class: "input input-primary", - placeholder: gettext("title") + placeholder: gettext("slug") ) %> - <%= error_tag(f, :title) %> + <%= error_tag(f, :slug) %> <%= textarea(f, :description, id: "pipeline-form-description", diff --git a/lib/memex_web/live/pipeline_live/index.ex b/lib/memex_web/live/pipeline_live/index.ex index c354a08..fb19084 100644 --- a/lib/memex_web/live/pipeline_live/index.ex +++ b/lib/memex_web/live/pipeline_live/index.ex @@ -16,11 +16,11 @@ defmodule MemexWeb.PipelineLive.Index do {:noreply, apply_action(socket, live_action, params)} end - defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do - %{title: title} = pipeline = Pipelines.get_pipeline!(id, current_user) + defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"slug" => slug}) do + %{slug: slug} = pipeline = Pipelines.get_pipeline_by_slug(slug, current_user) socket - |> assign(page_title: gettext("edit %{title}", title: title)) + |> assign(page_title: gettext("edit %{slug}", slug: slug)) |> assign(pipeline: pipeline) end @@ -49,12 +49,12 @@ defmodule MemexWeb.PipelineLive.Index do @impl true def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do pipeline = Pipelines.get_pipeline!(id, current_user) - {:ok, %{title: title}} = Pipelines.delete_pipeline(pipeline, current_user) + {:ok, %{slug: slug}} = Pipelines.delete_pipeline(pipeline, current_user) socket = socket |> assign(pipelines: Pipelines.list_pipelines(current_user)) - |> put_flash(:info, gettext("%{title} deleted", title: title)) + |> put_flash(:info, gettext("%{slug} deleted", slug: slug)) {:noreply, socket} end diff --git a/lib/memex_web/live/pipeline_live/index.html.heex b/lib/memex_web/live/pipeline_live/index.html.heex index 58a774f..6ef21f3 100644 --- a/lib/memex_web/live/pipeline_live/index.html.heex +++ b/lib/memex_web/live/pipeline_live/index.html.heex @@ -32,7 +32,7 @@ <:actions :let={pipeline}> <%= if is_owner?(pipeline, @current_user) do %> <.link - patch={Routes.pipeline_index_path(@socket, :edit, pipeline)} + patch={Routes.pipeline_index_path(@socket, :edit, pipeline.slug)} data-qa={"pipeline-edit-#{pipeline.id}"} > <%= dgettext("actions", "edit") %> diff --git a/lib/memex_web/live/pipeline_live/show.ex b/lib/memex_web/live/pipeline_live/show.ex index 7e76ae1..bc2a609 100644 --- a/lib/memex_web/live/pipeline_live/show.ex +++ b/lib/memex_web/live/pipeline_live/show.ex @@ -10,11 +10,15 @@ defmodule MemexWeb.PipelineLive.Show do @impl true def handle_params( - %{"id" => id}, + %{"slug" => slug}, _, %{assigns: %{live_action: live_action, current_user: current_user}} = socket ) do - pipeline = Pipelines.get_pipeline!(id, current_user) + pipeline = + case Pipelines.get_pipeline_by_slug(slug, current_user) do + nil -> raise MemexWeb.NotFoundError, gettext("%{slug} could not be found", slug: slug) + pipeline -> pipeline + end socket = socket @@ -30,18 +34,18 @@ defmodule MemexWeb.PipelineLive.Show do _params, %{assigns: %{pipeline: pipeline, current_user: current_user}} = socket ) do - {:ok, %{title: title}} = Pipelines.delete_pipeline(pipeline, current_user) + {:ok, %{slug: slug}} = Pipelines.delete_pipeline(pipeline, current_user) socket = socket - |> put_flash(:info, gettext("%{title} deleted", title: title)) + |> put_flash(:info, gettext("%{slug} deleted", slug: slug)) |> push_navigate(to: Routes.pipeline_index_path(Endpoint, :index)) {:noreply, socket} end - defp page_title(:show, %{title: title}), do: title - defp page_title(:edit, %{title: title}), do: gettext("edit %{title}", title: title) + defp page_title(:show, %{slug: slug}), do: slug + defp page_title(:edit, %{slug: slug}), do: gettext("edit %{slug}", slug: slug) @spec is_owner_or_admin?(Pipeline.t(), User.t()) :: boolean() defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true diff --git a/lib/memex_web/live/pipeline_live/show.html.heex b/lib/memex_web/live/pipeline_live/show.html.heex index 3c9b041..102e4b4 100644 --- a/lib/memex_web/live/pipeline_live/show.html.heex +++ b/lib/memex_web/live/pipeline_live/show.html.heex @@ -1,6 +1,6 @@

- <%= @pipeline.title %> + <%= @pipeline.slug %>

<%= if @pipeline.tags, do: @pipeline.tags |> Enum.join(", ") %>

@@ -23,7 +23,10 @@ <%= dgettext("actions", "back") %> <%= if is_owner?(@pipeline, @current_user) do %> - <.link class="btn btn-primary" patch={Routes.pipeline_show_path(@socket, :edit, @pipeline)}> + <.link + class="btn btn-primary" + patch={Routes.pipeline_show_path(@socket, :edit, @pipeline.slug)} + > <%= dgettext("actions", "edit") %> <% end %> @@ -42,7 +45,7 @@
<%= if @live_action in [:edit] do %> - <.modal return_to={Routes.pipeline_show_path(@socket, :show, @pipeline)}> + <.modal return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}> <.live_component module={MemexWeb.PipelineLive.FormComponent} id={@pipeline.id} @@ -50,7 +53,7 @@ title={@page_title} action={@live_action} pipeline={@pipeline} - return_to={Routes.pipeline_show_path(@socket, :show, @pipeline)} + return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)} /> <% end %> diff --git a/lib/memex_web/not_found_error.ex b/lib/memex_web/not_found_error.ex new file mode 100644 index 0000000..0dd662b --- /dev/null +++ b/lib/memex_web/not_found_error.ex @@ -0,0 +1,3 @@ +defmodule MemexWeb.NotFoundError do + defexception [:message, plug_status: 404] +end diff --git a/lib/memex_web/router.ex b/lib/memex_web/router.ex index 092d626..2c070fc 100644 --- a/lib/memex_web/router.ex +++ b/lib/memex_web/router.ex @@ -58,16 +58,16 @@ defmodule MemexWeb.Router do pipe_through [:browser, :require_authenticated_user] live "/notes/new", NoteLive.Index, :new - live "/notes/:id/edit", NoteLive.Index, :edit - live "/note/:id/edit", NoteLive.Show, :edit + live "/notes/:slug/edit", NoteLive.Index, :edit + live "/note/:slug/edit", NoteLive.Show, :edit live "/contexts/new", ContextLive.Index, :new - live "/contexts/:id/edit", ContextLive.Index, :edit - live "/context/:id/edit", ContextLive.Show, :edit + live "/contexts/:slug/edit", ContextLive.Index, :edit + live "/context/:slug/edit", ContextLive.Show, :edit live "/pipelines/new", PipelineLive.Index, :new - live "/pipelines/:id/edit", PipelineLive.Index, :edit - live "/pipelines/:id/show/edit", PipelineLive.Show, :edit + live "/pipelines/:slug/edit", PipelineLive.Index, :edit + live "/pipeline/:slug/edit", PipelineLive.Show, :edit get "/users/settings", UserSettingsController, :edit put "/users/settings", UserSettingsController, :update @@ -80,15 +80,15 @@ defmodule MemexWeb.Router do live "/notes", NoteLive.Index, :index live "/notes/:search", NoteLive.Index, :search - live "/note/:id", NoteLive.Show, :show + live "/note/:slug", NoteLive.Show, :show live "/contexts", ContextLive.Index, :index live "/contexts/:search", ContextLive.Index, :search - live "/context/:id", ContextLive.Show, :show + live "/context/:slug", ContextLive.Show, :show live "/pipelines", PipelineLive.Index, :index live "/pipelines/:search", PipelineLive.Index, :search - live "/pipeline/:id", PipelineLive.Show, :show + live "/pipeline/:slug", PipelineLive.Show, :show end end diff --git a/lib/memex_web/templates/error/error.html.heex b/lib/memex_web/templates/error/error.html.heex index a9a3924..755e0d9 100644 --- a/lib/memex_web/templates/error/error.html.heex +++ b/lib/memex_web/templates/error/error.html.heex @@ -11,7 +11,7 @@ - +
<.topbar current_user={assigns[:current_user]}>
@@ -25,7 +25,7 @@
- <%= dgettext("errors", "Go back home") %> + <%= dgettext("errors", "go back home") %> diff --git a/lib/memex_web/views/error_view.ex b/lib/memex_web/views/error_view.ex index 677eccd..7560511 100644 --- a/lib/memex_web/views/error_view.ex +++ b/lib/memex_web/views/error_view.ex @@ -6,9 +6,9 @@ defmodule MemexWeb.ErrorView do def template_not_found(error_path, _assigns) do error_string = case error_path do - "404.html" -> dgettext("errors", "Not found") - "401.html" -> dgettext("errors", "Unauthorized") - _ -> dgettext("errors", "Internal Server Error") + "404.html" -> dgettext("errors", "not found") + "401.html" -> dgettext("errors", "unauthorized") + _ -> dgettext("errors", "internal server error") end render("error.html", %{error_string: error_string}) diff --git a/priv/gettext/actions.pot b/priv/gettext/actions.pot index e45d3af..ac59067 100644 --- a/priv/gettext/actions.pot +++ b/priv/gettext/actions.pot @@ -68,11 +68,11 @@ msgid "create invite" msgstr "" #: lib/memex_web/live/context_live/index.html.heex:49 -#: lib/memex_web/live/context_live/show.html.heex:38 +#: lib/memex_web/live/context_live/show.html.heex:41 #: lib/memex_web/live/note_live/index.html.heex:49 #: lib/memex_web/live/note_live/show.html.heex:38 #: lib/memex_web/live/pipeline_live/index.html.heex:49 -#: lib/memex_web/live/pipeline_live/show.html.heex:38 +#: lib/memex_web/live/pipeline_live/show.html.heex:41 #, elixir-autogen, elixir-format msgid "delete" msgstr "" @@ -83,11 +83,11 @@ msgid "delete user" msgstr "" #: lib/memex_web/live/context_live/index.html.heex:38 -#: lib/memex_web/live/context_live/show.html.heex:27 +#: lib/memex_web/live/context_live/show.html.heex:30 #: lib/memex_web/live/note_live/index.html.heex:38 #: lib/memex_web/live/note_live/show.html.heex:27 #: lib/memex_web/live/pipeline_live/index.html.heex:38 -#: lib/memex_web/live/pipeline_live/show.html.heex:27 +#: lib/memex_web/live/pipeline_live/show.html.heex:30 #, elixir-autogen, elixir-format msgid "edit" msgstr "" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index c7d922e..d57984b 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -10,30 +10,6 @@ msgid "" msgstr "" -#: lib/memex_web/live/context_live/form_component.ex:61 -#: lib/memex_web/live/note_live/form_component.ex:60 -#: lib/memex_web/live/pipeline_live/form_component.ex:65 -#, elixir-autogen, elixir-format -msgid "%{title} created" -msgstr "" - -#: lib/memex_web/live/context_live/index.ex:57 -#: lib/memex_web/live/context_live/show.ex:37 -#: lib/memex_web/live/note_live/index.ex:57 -#: lib/memex_web/live/note_live/show.ex:37 -#: lib/memex_web/live/pipeline_live/index.ex:57 -#: lib/memex_web/live/pipeline_live/show.ex:37 -#, elixir-autogen, elixir-format -msgid "%{title} deleted" -msgstr "" - -#: lib/memex_web/live/context_live/form_component.ex:44 -#: lib/memex_web/live/note_live/form_component.ex:43 -#: lib/memex_web/live/pipeline_live/form_component.ex:48 -#, elixir-autogen, elixir-format -msgid "%{title} saved" -msgstr "" - #: lib/memex_web/live/invite_live/index.html.heex:90 #, elixir-autogen, elixir-format msgid "Admins" @@ -183,16 +159,6 @@ msgstr "" msgid "document your processes, attaching contexts to each step" msgstr "" -#: lib/memex_web/live/context_live/index.ex:23 -#: lib/memex_web/live/context_live/show.ex:44 -#: lib/memex_web/live/note_live/index.ex:23 -#: lib/memex_web/live/note_live/show.ex:44 -#: lib/memex_web/live/pipeline_live/index.ex:23 -#: lib/memex_web/live/pipeline_live/show.ex:44 -#, elixir-autogen, elixir-format -msgid "edit %{title}" -msgstr "" - #: lib/memex_web/live/invite_live/index.ex:33 #, elixir-autogen, elixir-format msgid "edit invite" @@ -392,16 +358,6 @@ msgstr "" msgid "tags" msgstr "" -#: lib/memex_web/components/contexts_table_component.ex:47 -#: lib/memex_web/components/notes_table_component.ex:47 -#: lib/memex_web/components/pipelines_table_component.ex:47 -#: lib/memex_web/live/context_live/form_component.html.heex:14 -#: lib/memex_web/live/note_live/form_component.html.heex:14 -#: lib/memex_web/live/pipeline_live/form_component.html.heex:14 -#, elixir-autogen, elixir-format -msgid "title" -msgstr "" - #: lib/memex_web/components/invite_card.ex:20 #, elixir-autogen, elixir-format msgid "unlimited" @@ -476,3 +432,54 @@ msgstr "" #, elixir-autogen, elixir-format msgid "no pipelines found" msgstr "" + +#: lib/memex_web/live/context_live/form_component.ex:61 +#: lib/memex_web/live/note_live/form_component.ex:60 +#: lib/memex_web/live/pipeline_live/form_component.ex:65 +#, elixir-autogen, elixir-format +msgid "%{slug} created" +msgstr "" + +#: lib/memex_web/live/context_live/index.ex:57 +#: lib/memex_web/live/context_live/show.ex:41 +#: lib/memex_web/live/note_live/index.ex:57 +#: lib/memex_web/live/note_live/show.ex:41 +#: lib/memex_web/live/pipeline_live/index.ex:57 +#: lib/memex_web/live/pipeline_live/show.ex:41 +#, elixir-autogen, elixir-format +msgid "%{slug} deleted" +msgstr "" + +#: lib/memex_web/live/context_live/form_component.ex:44 +#: lib/memex_web/live/note_live/form_component.ex:43 +#: lib/memex_web/live/pipeline_live/form_component.ex:48 +#, elixir-autogen, elixir-format +msgid "%{slug} saved" +msgstr "" + +#: lib/memex_web/live/context_live/index.ex:23 +#: lib/memex_web/live/context_live/show.ex:48 +#: lib/memex_web/live/note_live/index.ex:23 +#: lib/memex_web/live/note_live/show.ex:48 +#: lib/memex_web/live/pipeline_live/index.ex:23 +#: lib/memex_web/live/pipeline_live/show.ex:48 +#, elixir-autogen, elixir-format +msgid "edit %{slug}" +msgstr "" + +#: lib/memex_web/components/contexts_table_component.ex:47 +#: lib/memex_web/components/notes_table_component.ex:47 +#: lib/memex_web/components/pipelines_table_component.ex:47 +#: lib/memex_web/live/context_live/form_component.html.heex:14 +#: lib/memex_web/live/note_live/form_component.html.heex:14 +#: lib/memex_web/live/pipeline_live/form_component.html.heex:14 +#, elixir-autogen, elixir-format +msgid "slug" +msgstr "" + +#: lib/memex_web/live/context_live/show.ex:19 +#: lib/memex_web/live/note_live/show.ex:19 +#: lib/memex_web/live/pipeline_live/show.ex:19 +#, elixir-autogen, elixir-format +msgid "%{slug} could not be found" +msgstr "" diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index 0e5a4bd..837d41d 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -20,26 +20,11 @@ msgstr "" msgid "Error" msgstr "" -#: lib/memex_web/templates/error/error.html.heex:28 -#, elixir-autogen, elixir-format -msgid "Go back home" -msgstr "" - -#: lib/memex_web/views/error_view.ex:11 -#, elixir-autogen, elixir-format -msgid "Internal Server Error" -msgstr "" - #: lib/memex_web/controllers/user_session_controller.ex:17 #, elixir-autogen, elixir-format msgid "Invalid email or password" msgstr "" -#: lib/memex_web/views/error_view.ex:9 -#, elixir-autogen, elixir-format -msgid "Not found" -msgstr "" - #: lib/memex_web/templates/user_registration/new.html.heex:15 #: lib/memex_web/templates/user_reset_password/edit.html.heex:15 #: lib/memex_web/templates/user_settings/edit.html.heex:64 @@ -69,11 +54,6 @@ msgstr "" msgid "Unable to delete user" msgstr "" -#: lib/memex_web/views/error_view.ex:10 -#, elixir-autogen, elixir-format -msgid "Unauthorized" -msgstr "" - #: lib/memex_web/controllers/user_confirmation_controller.ex:54 #, elixir-autogen, elixir-format msgid "User confirmation link is invalid or it has expired." @@ -120,3 +100,33 @@ msgstr "" #, elixir-autogen, elixir-format msgid "oops, something went wrong! Please check the errors below" msgstr "" + +#: lib/memex/contexts/context.ex:49 +#: lib/memex/contexts/context.ex:60 +#: lib/memex/notes/note.ex:48 +#: lib/memex/notes/note.ex:59 +#: lib/memex/pipelines/pipeline.ex:50 +#: lib/memex/pipelines/pipeline.ex:61 +#, elixir-autogen, elixir-format +msgid "invalid format: only numbers, letters and hyphen are accepted" +msgstr "" + +#: lib/memex_web/templates/error/error.html.heex:28 +#, elixir-autogen, elixir-format +msgid "go back home" +msgstr "" + +#: lib/memex_web/views/error_view.ex:11 +#, elixir-autogen, elixir-format +msgid "internal server error" +msgstr "" + +#: lib/memex_web/views/error_view.ex:9 +#, elixir-autogen, elixir-format +msgid "not found" +msgstr "" + +#: lib/memex_web/views/error_view.ex:10 +#, elixir-autogen, elixir-format +msgid "unauthorized" +msgstr "" diff --git a/priv/gettext/prompts.pot b/priv/gettext/prompts.pot index 31359f8..00fbc40 100644 --- a/priv/gettext/prompts.pot +++ b/priv/gettext/prompts.pot @@ -142,11 +142,11 @@ msgid "are you sure you want to make %{invite_name} unlimited?" msgstr "" #: lib/memex_web/live/context_live/index.html.heex:46 -#: lib/memex_web/live/context_live/show.html.heex:35 +#: lib/memex_web/live/context_live/show.html.heex:38 #: lib/memex_web/live/note_live/index.html.heex:46 #: lib/memex_web/live/note_live/show.html.heex:35 #: lib/memex_web/live/pipeline_live/index.html.heex:46 -#: lib/memex_web/live/pipeline_live/show.html.heex:35 +#: lib/memex_web/live/pipeline_live/show.html.heex:38 #, elixir-autogen, elixir-format msgid "are you sure?" msgstr "" diff --git a/priv/repo/migrations/20221126182210_use_slugs.exs b/priv/repo/migrations/20221126182210_use_slugs.exs new file mode 100644 index 0000000..e08fb27 --- /dev/null +++ b/priv/repo/migrations/20221126182210_use_slugs.exs @@ -0,0 +1,14 @@ +defmodule Memex.Repo.Migrations.UseSlugs do + use Ecto.Migration + + def change do + rename table(:notes), :title, to: :slug + create unique_index(:notes, [:slug]) + + rename table(:contexts), :title, to: :slug + create unique_index(:contexts, [:slug]) + + rename table(:pipelines), :title, to: :slug + create unique_index(:pipelines, [:slug]) + end +end diff --git a/test/memex/contexts_test.exs b/test/memex/contexts_test.exs index 69438dd..c4da056 100644 --- a/test/memex/contexts_test.exs +++ b/test/memex/contexts_test.exs @@ -3,7 +3,7 @@ defmodule Memex.ContextsTest do import Memex.ContextsFixtures alias Memex.{Contexts, Contexts.Context} @moduletag :contexts_test - @invalid_attrs %{content: nil, tag: nil, title: nil, visibility: nil} + @invalid_attrs %{content: nil, tag: nil, slug: nil, visibility: nil} describe "contexts" do setup do @@ -11,9 +11,9 @@ defmodule Memex.ContextsTest do end test "list_contexts/1 returns all contexts for a user", %{user: user} do - context_a = context_fixture(%{title: "a", visibility: :public}, user) - context_b = context_fixture(%{title: "b", visibility: :unlisted}, user) - context_c = context_fixture(%{title: "c", visibility: :private}, user) + context_a = context_fixture(%{slug: "a", visibility: :public}, user) + context_b = context_fixture(%{slug: "b", visibility: :unlisted}, user) + context_c = context_fixture(%{slug: "c", visibility: :private}, user) assert Contexts.list_contexts(user) == [context_a, context_b, context_c] end @@ -50,18 +50,43 @@ defmodule Memex.ContextsTest do end end + test "get_context_by_slug/1 returns the context with given id", %{user: user} do + context = context_fixture(%{slug: "a", visibility: :public}, user) + assert Contexts.get_context_by_slug("a", user) == context + + context = context_fixture(%{slug: "b", visibility: :unlisted}, user) + assert Contexts.get_context_by_slug("b", user) == context + + context = context_fixture(%{slug: "c", visibility: :private}, user) + assert Contexts.get_context_by_slug("c", user) == context + end + + test "get_context_by_slug/1 only returns unlisted or public contexts for other users", %{ + user: user + } do + another_user = user_fixture() + context = context_fixture(%{slug: "a", visibility: :public}, another_user) + assert Contexts.get_context_by_slug("a", user) == context + + context = context_fixture(%{slug: "b", visibility: :unlisted}, another_user) + assert Contexts.get_context_by_slug("b", user) == context + + context_fixture(%{slug: "c", visibility: :private}, another_user) + assert Contexts.get_context_by_slug("c", user) |> is_nil() + end + test "create_context/1 with valid data creates a context", %{user: user} do valid_attrs = %{ "content" => "some content", "tags_string" => "tag1,tag2", - "title" => "some title", + "slug" => "some-slug", "visibility" => :public } assert {:ok, %Context{} = context} = Contexts.create_context(valid_attrs, user) assert context.content == "some content" assert context.tags == ["tag1", "tag2"] - assert context.title == "some title" + assert context.slug == "some-slug" assert context.visibility == :public end @@ -75,14 +100,14 @@ defmodule Memex.ContextsTest do update_attrs = %{ "content" => "some updated content", "tags_string" => "tag1,tag2", - "title" => "some updated title", + "slug" => "some-updated-slug", "visibility" => :private } assert {:ok, %Context{} = context} = Contexts.update_context(context, update_attrs, user) assert context.content == "some updated content" assert context.tags == ["tag1", "tag2"] - assert context.title == "some updated title" + assert context.slug == "some-updated-slug" assert context.visibility == :private end diff --git a/test/memex/notes_test.exs b/test/memex/notes_test.exs index bbb2663..788505b 100644 --- a/test/memex/notes_test.exs +++ b/test/memex/notes_test.exs @@ -3,7 +3,7 @@ defmodule Memex.NotesTest do import Memex.NotesFixtures alias Memex.{Notes, Notes.Note} @moduletag :notes_test - @invalid_attrs %{content: nil, tag: nil, title: nil, visibility: nil} + @invalid_attrs %{content: nil, tag: nil, slug: nil, visibility: nil} describe "notes" do setup do @@ -11,9 +11,9 @@ defmodule Memex.NotesTest do end test "list_notes/1 returns all notes for a user", %{user: user} do - note_a = note_fixture(%{title: "a", visibility: :public}, user) - note_b = note_fixture(%{title: "b", visibility: :unlisted}, user) - note_c = note_fixture(%{title: "c", visibility: :private}, user) + note_a = note_fixture(%{slug: "a", visibility: :public}, user) + note_b = note_fixture(%{slug: "b", visibility: :unlisted}, user) + note_c = note_fixture(%{slug: "c", visibility: :private}, user) assert Notes.list_notes(user) == [note_a, note_b, note_c] end @@ -50,18 +50,43 @@ defmodule Memex.NotesTest do end end + test "get_note_by_slug/1 returns the note with given id", %{user: user} do + note = note_fixture(%{slug: "a", visibility: :public}, user) + assert Notes.get_note_by_slug("a", user) == note + + note = note_fixture(%{slug: "b", visibility: :unlisted}, user) + assert Notes.get_note_by_slug("b", user) == note + + note = note_fixture(%{slug: "c", visibility: :private}, user) + assert Notes.get_note_by_slug("c", user) == note + end + + test "get_note_by_slug/1 only returns unlisted or public notes for other users", %{ + user: user + } do + another_user = user_fixture() + note = note_fixture(%{slug: "a", visibility: :public}, another_user) + assert Notes.get_note_by_slug("a", user) == note + + note = note_fixture(%{slug: "b", visibility: :unlisted}, another_user) + assert Notes.get_note_by_slug("b", user) == note + + note_fixture(%{slug: "c", visibility: :private}, another_user) + assert Notes.get_note_by_slug("c", user) |> is_nil() + end + test "create_note/1 with valid data creates a note", %{user: user} do valid_attrs = %{ "content" => "some content", "tags_string" => "tag1,tag2", - "title" => "some title", + "slug" => "some-slug", "visibility" => :public } assert {:ok, %Note{} = note} = Notes.create_note(valid_attrs, user) assert note.content == "some content" assert note.tags == ["tag1", "tag2"] - assert note.title == "some title" + assert note.slug == "some-slug" assert note.visibility == :public end @@ -75,14 +100,14 @@ defmodule Memex.NotesTest do update_attrs = %{ "content" => "some updated content", "tags_string" => "tag1,tag2", - "title" => "some updated title", + "slug" => "some-updated-slug", "visibility" => :private } assert {:ok, %Note{} = note} = Notes.update_note(note, update_attrs, user) assert note.content == "some updated content" assert note.tags == ["tag1", "tag2"] - assert note.title == "some updated title" + assert note.slug == "some-updated-slug" assert note.visibility == :private end diff --git a/test/memex/pipelines_test.exs b/test/memex/pipelines_test.exs index 8746d6c..074a553 100644 --- a/test/memex/pipelines_test.exs +++ b/test/memex/pipelines_test.exs @@ -3,7 +3,7 @@ defmodule Memex.PipelinesTest do import Memex.PipelinesFixtures alias Memex.{Pipelines, Pipelines.Pipeline} @moduletag :pipelines_test - @invalid_attrs %{description: nil, tag: nil, title: nil, visibility: nil} + @invalid_attrs %{description: nil, tag: nil, slug: nil, visibility: nil} describe "pipelines" do setup do @@ -11,9 +11,9 @@ defmodule Memex.PipelinesTest do end test "list_pipelines/1 returns all pipelines for a user", %{user: user} do - pipeline_a = pipeline_fixture(%{title: "a", visibility: :public}, user) - pipeline_b = pipeline_fixture(%{title: "b", visibility: :unlisted}, user) - pipeline_c = pipeline_fixture(%{title: "c", visibility: :private}, user) + pipeline_a = pipeline_fixture(%{slug: "a", visibility: :public}, user) + pipeline_b = pipeline_fixture(%{slug: "b", visibility: :unlisted}, user) + pipeline_c = pipeline_fixture(%{slug: "c", visibility: :private}, user) assert Pipelines.list_pipelines(user) == [pipeline_a, pipeline_b, pipeline_c] end @@ -52,18 +52,43 @@ defmodule Memex.PipelinesTest do end end + test "get_pipeline_by_slug/1 returns the pipeline with given id", %{user: user} do + pipeline = pipeline_fixture(%{slug: "a", visibility: :public}, user) + assert Pipelines.get_pipeline_by_slug("a", user) == pipeline + + pipeline = pipeline_fixture(%{slug: "b", visibility: :unlisted}, user) + assert Pipelines.get_pipeline_by_slug("b", user) == pipeline + + pipeline = pipeline_fixture(%{slug: "c", visibility: :private}, user) + assert Pipelines.get_pipeline_by_slug("c", user) == pipeline + end + + test "get_pipeline_by_slug/1 only returns unlisted or public pipelines for other users", %{ + user: user + } do + another_user = user_fixture() + pipeline = pipeline_fixture(%{slug: "a", visibility: :public}, another_user) + assert Pipelines.get_pipeline_by_slug("a", user) == pipeline + + pipeline = pipeline_fixture(%{slug: "b", visibility: :unlisted}, another_user) + assert Pipelines.get_pipeline_by_slug("b", user) == pipeline + + pipeline_fixture(%{slug: "c", visibility: :private}, another_user) + assert Pipelines.get_pipeline_by_slug("c", user) |> is_nil() + end + test "create_pipeline/1 with valid data creates a pipeline", %{user: user} do valid_attrs = %{ "description" => "some description", "tags_string" => "tag1,tag2", - "title" => "some title", + "slug" => "some-slug", "visibility" => :public } assert {:ok, %Pipeline{} = pipeline} = Pipelines.create_pipeline(valid_attrs, user) assert pipeline.description == "some description" assert pipeline.tags == ["tag1", "tag2"] - assert pipeline.title == "some title" + assert pipeline.slug == "some-slug" assert pipeline.visibility == :public end @@ -77,7 +102,7 @@ defmodule Memex.PipelinesTest do update_attrs = %{ "description" => "some updated description", "tags_string" => "tag1,tag2", - "title" => "some updated title", + "slug" => "some-updated-slug", "visibility" => :private } @@ -86,7 +111,7 @@ defmodule Memex.PipelinesTest do assert pipeline.description == "some updated description" assert pipeline.tags == ["tag1", "tag2"] - assert pipeline.title == "some updated title" + assert pipeline.slug == "some-updated-slug" assert pipeline.visibility == :private end diff --git a/test/memex_web/live/context_live_test.exs b/test/memex_web/live/context_live_test.exs index c2d83dc..5837cd7 100644 --- a/test/memex_web/live/context_live_test.exs +++ b/test/memex_web/live/context_live_test.exs @@ -7,19 +7,19 @@ defmodule MemexWeb.ContextLiveTest do @create_attrs %{ "content" => "some content", "tags_string" => "tag1", - "title" => "some title", + "slug" => "some-slug", "visibility" => :public } @update_attrs %{ "content" => "some updated content", "tags_string" => "tag1,tag2", - "title" => "some updated title", + "slug" => "some-updated-slug", "visibility" => :private } @invalid_attrs %{ "content" => nil, "tags_string" => "", - "title" => nil, + "slug" => nil, "visibility" => nil } @@ -55,7 +55,7 @@ defmodule MemexWeb.ContextLiveTest do |> render_submit() |> follow_redirect(conn, Routes.context_index_path(conn, :index)) - assert html =~ "#{@create_attrs |> Map.get("title")} created" + assert html =~ "#{@create_attrs |> Map.get("slug")} created" assert html =~ "some content" end @@ -65,7 +65,7 @@ defmodule MemexWeb.ContextLiveTest do assert index_live |> element("[data-qa=\"context-edit-#{context.id}\"]") |> render_click() =~ "edit" - assert_patch(index_live, Routes.context_index_path(conn, :edit, context)) + assert_patch(index_live, Routes.context_index_path(conn, :edit, context.slug)) assert index_live |> form("#context-form", context: @invalid_attrs) @@ -77,7 +77,7 @@ defmodule MemexWeb.ContextLiveTest do |> render_submit() |> follow_redirect(conn, Routes.context_index_path(conn, :index)) - assert html =~ "#{@update_attrs |> Map.get("title")} saved" + assert html =~ "#{@update_attrs |> Map.get("slug")} saved" assert html =~ "some updated content" end @@ -93,18 +93,18 @@ defmodule MemexWeb.ContextLiveTest do setup [:register_and_log_in_user, :create_context] test "displays context", %{conn: conn, context: context} do - {:ok, _show_live, html} = live(conn, Routes.context_show_path(conn, :show, context)) + {:ok, _show_live, html} = live(conn, Routes.context_show_path(conn, :show, context.slug)) assert html =~ "context" assert html =~ context.content end test "updates context within modal", %{conn: conn, context: context} do - {:ok, show_live, _html} = live(conn, Routes.context_show_path(conn, :show, context)) + {:ok, show_live, _html} = live(conn, Routes.context_show_path(conn, :show, context.slug)) assert show_live |> element("a", "edit") |> render_click() =~ "edit" - assert_patch(show_live, Routes.context_show_path(conn, :edit, context)) + assert_patch(show_live, Routes.context_show_path(conn, :edit, context.slug)) assert show_live |> form("#context-form", context: @invalid_attrs) @@ -112,16 +112,16 @@ defmodule MemexWeb.ContextLiveTest do {:ok, _, html} = show_live - |> form("#context-form", context: @update_attrs) + |> form("#context-form", context: Map.put(@update_attrs, "slug", context.slug)) |> render_submit() - |> follow_redirect(conn, Routes.context_show_path(conn, :show, context)) + |> follow_redirect(conn, Routes.context_show_path(conn, :show, context.slug)) - assert html =~ "#{@update_attrs |> Map.get("title")} saved" + assert html =~ "#{context.slug} saved" assert html =~ "some updated content" end test "deletes context", %{conn: conn, context: context} do - {:ok, show_live, _html} = live(conn, Routes.context_show_path(conn, :show, context)) + {:ok, show_live, _html} = live(conn, Routes.context_show_path(conn, :show, context.slug)) {:ok, index_live, _html} = show_live diff --git a/test/memex_web/live/note_live_test.exs b/test/memex_web/live/note_live_test.exs index 46a9716..d93aebc 100644 --- a/test/memex_web/live/note_live_test.exs +++ b/test/memex_web/live/note_live_test.exs @@ -7,19 +7,19 @@ defmodule MemexWeb.NoteLiveTest do @create_attrs %{ "content" => "some content", "tags_string" => "tag1", - "title" => "some title", + "slug" => "some-slug", "visibility" => :public } @update_attrs %{ "content" => "some updated content", "tags_string" => "tag1,tag2", - "title" => "some updated title", + "slug" => "some-updated-slug", "visibility" => :private } @invalid_attrs %{ "content" => nil, "tags_string" => "", - "title" => nil, + "slug" => nil, "visibility" => nil } @@ -55,7 +55,7 @@ defmodule MemexWeb.NoteLiveTest do |> render_submit() |> follow_redirect(conn, Routes.note_index_path(conn, :index)) - assert html =~ "#{@create_attrs |> Map.get("title")} created" + assert html =~ "#{@create_attrs |> Map.get("slug")} created" assert html =~ "some content" end @@ -65,7 +65,7 @@ defmodule MemexWeb.NoteLiveTest do assert index_live |> element("[data-qa=\"note-edit-#{note.id}\"]") |> render_click() =~ "edit" - assert_patch(index_live, Routes.note_index_path(conn, :edit, note)) + assert_patch(index_live, Routes.note_index_path(conn, :edit, note.slug)) assert index_live |> form("#note-form", note: @invalid_attrs) @@ -77,7 +77,7 @@ defmodule MemexWeb.NoteLiveTest do |> render_submit() |> follow_redirect(conn, Routes.note_index_path(conn, :index)) - assert html =~ "#{@update_attrs |> Map.get("title")} saved" + assert html =~ "#{@update_attrs |> Map.get("slug")} saved" assert html =~ "some updated content" end @@ -93,18 +93,18 @@ defmodule MemexWeb.NoteLiveTest do setup [:register_and_log_in_user, :create_note] test "displays note", %{conn: conn, note: note} do - {:ok, _show_live, html} = live(conn, Routes.note_show_path(conn, :show, note)) + {:ok, _show_live, html} = live(conn, Routes.note_show_path(conn, :show, note.slug)) assert html =~ "note" assert html =~ note.content end test "updates note within modal", %{conn: conn, note: note} do - {:ok, show_live, _html} = live(conn, Routes.note_show_path(conn, :show, note)) + {:ok, show_live, _html} = live(conn, Routes.note_show_path(conn, :show, note.slug)) assert show_live |> element("a", "edit") |> render_click() =~ "edit" - assert_patch(show_live, Routes.note_show_path(conn, :edit, note)) + assert_patch(show_live, Routes.note_show_path(conn, :edit, note.slug)) assert show_live |> form("#note-form", note: @invalid_attrs) @@ -112,16 +112,16 @@ defmodule MemexWeb.NoteLiveTest do {:ok, _, html} = show_live - |> form("#note-form", note: @update_attrs) + |> form("#note-form", note: Map.put(@update_attrs, "slug", note.slug)) |> render_submit() - |> follow_redirect(conn, Routes.note_show_path(conn, :show, note)) + |> follow_redirect(conn, Routes.note_show_path(conn, :show, note.slug)) - assert html =~ "#{@update_attrs |> Map.get("title")} saved" + assert html =~ "#{note.slug} saved" assert html =~ "some updated content" end test "deletes note", %{conn: conn, note: note} do - {:ok, show_live, _html} = live(conn, Routes.note_show_path(conn, :show, note)) + {:ok, show_live, _html} = live(conn, Routes.note_show_path(conn, :show, note.slug)) {:ok, index_live, _html} = show_live diff --git a/test/memex_web/live/pipeline_live_test.exs b/test/memex_web/live/pipeline_live_test.exs index 587192f..afa41c3 100644 --- a/test/memex_web/live/pipeline_live_test.exs +++ b/test/memex_web/live/pipeline_live_test.exs @@ -7,19 +7,19 @@ defmodule MemexWeb.PipelineLiveTest do @create_attrs %{ "description" => "some description", "tags_string" => "tag1", - "title" => "some title", + "slug" => "some-slug", "visibility" => :public } @update_attrs %{ "description" => "some updated description", "tags_string" => "tag1,tag2", - "title" => "some updated title", + "slug" => "some-updated-slug", "visibility" => :private } @invalid_attrs %{ "description" => nil, "tags_string" => "", - "title" => nil, + "slug" => nil, "visibility" => nil } @@ -55,7 +55,7 @@ defmodule MemexWeb.PipelineLiveTest do |> render_submit() |> follow_redirect(conn, Routes.pipeline_index_path(conn, :index)) - assert html =~ "#{@create_attrs |> Map.get("title")} created" + assert html =~ "#{@create_attrs |> Map.get("slug")} created" assert html =~ "some description" end @@ -65,7 +65,7 @@ defmodule MemexWeb.PipelineLiveTest do assert index_live |> element("[data-qa=\"pipeline-edit-#{pipeline.id}\"]") |> render_click() =~ "edit" - assert_patch(index_live, Routes.pipeline_index_path(conn, :edit, pipeline)) + assert_patch(index_live, Routes.pipeline_index_path(conn, :edit, pipeline.slug)) assert index_live |> form("#pipeline-form", pipeline: @invalid_attrs) @@ -77,7 +77,7 @@ defmodule MemexWeb.PipelineLiveTest do |> render_submit() |> follow_redirect(conn, Routes.pipeline_index_path(conn, :index)) - assert html =~ "#{@update_attrs |> Map.get("title")} saved" + assert html =~ "#{@update_attrs |> Map.get("slug")} saved" assert html =~ "some updated description" end @@ -96,18 +96,18 @@ defmodule MemexWeb.PipelineLiveTest do setup [:register_and_log_in_user, :create_pipeline] test "displays pipeline", %{conn: conn, pipeline: pipeline} do - {:ok, _show_live, html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline)) + {:ok, _show_live, html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) assert html =~ "pipeline" assert html =~ pipeline.description end test "updates pipeline within modal", %{conn: conn, pipeline: pipeline} do - {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline)) + {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) assert show_live |> element("a", "edit") |> render_click() =~ "edit" - assert_patch(show_live, Routes.pipeline_show_path(conn, :edit, pipeline)) + assert_patch(show_live, Routes.pipeline_show_path(conn, :edit, pipeline.slug)) assert show_live |> form("#pipeline-form", pipeline: @invalid_attrs) @@ -115,16 +115,16 @@ defmodule MemexWeb.PipelineLiveTest do {:ok, _, html} = show_live - |> form("#pipeline-form", pipeline: @update_attrs) + |> form("#pipeline-form", pipeline: Map.put(@update_attrs, "slug", pipeline.slug)) |> render_submit() - |> follow_redirect(conn, Routes.pipeline_show_path(conn, :show, pipeline)) + |> follow_redirect(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) - assert html =~ "#{@update_attrs |> Map.get("title")} saved" + assert html =~ "#{pipeline.slug} saved" assert html =~ "some updated description" end test "deletes pipeline", %{conn: conn, pipeline: pipeline} do - {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline)) + {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) {:ok, index_live, _html} = show_live diff --git a/test/memex_web/views/error_view_test.exs b/test/memex_web/views/error_view_test.exs index f2ecd03..b4c0a4f 100644 --- a/test/memex_web/views/error_view_test.exs +++ b/test/memex_web/views/error_view_test.exs @@ -13,11 +13,11 @@ defmodule MemexWeb.ErrorViewTest do test "renders 404.html" do assert render_to_string(MemexWeb.ErrorView, "404.html", []) =~ - dgettext("errors", "Not found") + dgettext("errors", "not found") end test "renders 500.html" do assert render_to_string(MemexWeb.ErrorView, "500.html", []) =~ - dgettext("errors", "Internal Server Error") + dgettext("errors", "internal server error") end end diff --git a/test/support/fixtures.ex b/test/support/fixtures.ex index 13b3baa..6355a60 100644 --- a/test/support/fixtures.ex +++ b/test/support/fixtures.ex @@ -2,7 +2,6 @@ defmodule Memex.Fixtures do @moduledoc """ This module defines test helpers for creating entities """ - alias Memex.{Accounts, Accounts.User, Email, Repo} def unique_user_email, do: "user#{System.unique_integer()}@example.com" @@ -57,5 +56,14 @@ defmodule Memex.Fixtures do }) end + def random_slug(length \\ 20) do + symbols = '0123456789abcdef-' + symbol_count = Enum.count(symbols) + + for _ <- Range.new(1, length), + into: "", + do: <> + end + defp unwrap_ok_tuple({:ok, value}), do: value end diff --git a/test/support/fixtures/contexts_fixtures.ex b/test/support/fixtures/contexts_fixtures.ex index b930f21..f034543 100644 --- a/test/support/fixtures/contexts_fixtures.ex +++ b/test/support/fixtures/contexts_fixtures.ex @@ -3,6 +3,7 @@ defmodule Memex.ContextsFixtures do This module defines test helpers for creating entities via the `Memex.Contexts` context. """ + import Memex.Fixtures alias Memex.{Accounts.User, Contexts, Contexts.Context} @doc """ @@ -16,7 +17,7 @@ defmodule Memex.ContextsFixtures do |> Enum.into(%{ content: "some content", tag: [], - title: "some title", + slug: random_slug(), visibility: :private }) |> Contexts.create_context(user) diff --git a/test/support/fixtures/notes_fixtures.ex b/test/support/fixtures/notes_fixtures.ex index ee3266b..2c593e6 100644 --- a/test/support/fixtures/notes_fixtures.ex +++ b/test/support/fixtures/notes_fixtures.ex @@ -3,6 +3,7 @@ defmodule Memex.NotesFixtures do This module defines test helpers for creating entities via the `Memex.Notes` context. """ + import Memex.Fixtures alias Memex.{Accounts.User, Notes, Notes.Note} @doc """ @@ -16,7 +17,7 @@ defmodule Memex.NotesFixtures do |> Enum.into(%{ content: "some content", tag: [], - title: "some title", + slug: random_slug(), visibility: :private }) |> Notes.create_note(user) diff --git a/test/support/fixtures/pipelines_fixtures.ex b/test/support/fixtures/pipelines_fixtures.ex index 48168c1..2cd6069 100644 --- a/test/support/fixtures/pipelines_fixtures.ex +++ b/test/support/fixtures/pipelines_fixtures.ex @@ -3,6 +3,7 @@ defmodule Memex.PipelinesFixtures do This module defines test helpers for creating entities via the `Memex.Pipelines` context. """ + import Memex.Fixtures alias Memex.{Accounts.User, Pipelines, Pipelines.Pipeline} @doc """ @@ -16,7 +17,7 @@ defmodule Memex.PipelinesFixtures do |> Enum.into(%{ description: "some description", tag: [], - title: "some title", + slug: random_slug(), visibility: :private }) |> Pipelines.create_pipeline(user)