use slugs
This commit is contained in:
		| @@ -29,7 +29,9 @@ import topbar from '../vendor/topbar' | |||||||
| import MaintainAttrs from './maintain_attrs' | import MaintainAttrs from './maintain_attrs' | ||||||
| import Alpine from 'alpinejs' | 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, { | const liveSocket = new LiveSocket('/live', Socket, { | ||||||
|   dom: { |   dom: { | ||||||
|     onBeforeElUpdated (from, to) { |     onBeforeElUpdated (from, to) { | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ defmodule Memex.Contexts do | |||||||
|       [%Context{}, ...] |       [%Context{}, ...] | ||||||
|  |  | ||||||
|       iex> list_contexts("my context", %User{id: 123}) |       iex> list_contexts("my context", %User{id: 123}) | ||||||
|       [%Context{title: "my context"}, ...] |       [%Context{slug: "my context"}, ...] | ||||||
|  |  | ||||||
|   """ |   """ | ||||||
|   @spec list_contexts(User.t()) :: [Context.t()] |   @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 \\ nil, user) | ||||||
|  |  | ||||||
|   def list_contexts(search, %{id: user_id}) when search |> is_nil() or search == "" do |   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 |   end | ||||||
|  |  | ||||||
|   def list_contexts(search, %{id: user_id}) when search |> is_binary() do |   def list_contexts(search, %{id: user_id}) when search |> is_binary() do | ||||||
| @@ -57,7 +57,7 @@ defmodule Memex.Contexts do | |||||||
|       [%Context{}, ...] |       [%Context{}, ...] | ||||||
|  |  | ||||||
|       iex> list_public_contexts("my context") |       iex> list_public_contexts("my context") | ||||||
|       [%Context{title: "my context"}, ...] |       [%Context{slug: "my context"}, ...] | ||||||
|  |  | ||||||
|   """ |   """ | ||||||
|   @spec list_public_contexts() :: [Context.t()] |   @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 \\ nil) | ||||||
|  |  | ||||||
|   def list_public_contexts(search) when search |> is_nil() or search == "" do |   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 |   end | ||||||
|  |  | ||||||
|   def list_public_contexts(search) when search |> is_binary() do |   def list_public_contexts(search) when search |> is_binary() do | ||||||
| @@ -120,6 +120,37 @@ defmodule Memex.Contexts do | |||||||
|     ) |     ) | ||||||
|   end |   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 """ |   @doc """ | ||||||
|   Creates a context. |   Creates a context. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,19 +3,19 @@ defmodule Memex.Contexts.Context do | |||||||
|   Represents a document that synthesizes multiple concepts as defined by notes |   Represents a document that synthesizes multiple concepts as defined by notes | ||||||
|   into a single consideration |   into a single consideration | ||||||
|   """ |   """ | ||||||
|  |  | ||||||
|   use Ecto.Schema |   use Ecto.Schema | ||||||
|   import Ecto.Changeset |   import Ecto.Changeset | ||||||
|  |   import MemexWeb.Gettext | ||||||
|   alias Ecto.{Changeset, UUID} |   alias Ecto.{Changeset, UUID} | ||||||
|   alias Memex.Accounts.User |   alias Memex.Accounts.User | ||||||
|  |  | ||||||
|   @primary_key {:id, :binary_id, autogenerate: true} |   @primary_key {:id, :binary_id, autogenerate: true} | ||||||
|   @foreign_key_type :binary_id |   @foreign_key_type :binary_id | ||||||
|   schema "contexts" do |   schema "contexts" do | ||||||
|  |     field :slug, :string | ||||||
|     field :content, :string |     field :content, :string | ||||||
|     field :tags, {:array, :string} |     field :tags, {:array, :string} | ||||||
|     field :tags_string, :string, virtual: true |     field :tags_string, :string, virtual: true | ||||||
|     field :title, :string |  | ||||||
|     field :visibility, Ecto.Enum, values: [:public, :private, :unlisted] |     field :visibility, Ecto.Enum, values: [:public, :private, :unlisted] | ||||||
|  |  | ||||||
|     belongs_to :user, User |     belongs_to :user, User | ||||||
| @@ -24,7 +24,7 @@ defmodule Memex.Contexts.Context do | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   @type t :: %__MODULE__{ |   @type t :: %__MODULE__{ | ||||||
|           title: String.t(), |           slug: slug(), | ||||||
|           content: String.t(), |           content: String.t(), | ||||||
|           tags: [String.t()] | nil, |           tags: [String.t()] | nil, | ||||||
|           tags_string: String.t(), |           tags_string: String.t(), | ||||||
| @@ -35,24 +35,31 @@ defmodule Memex.Contexts.Context do | |||||||
|           updated_at: NaiveDateTime.t() |           updated_at: NaiveDateTime.t() | ||||||
|         } |         } | ||||||
|   @type id :: UUID.t() |   @type id :: UUID.t() | ||||||
|  |   @type slug :: String.t() | ||||||
|   @type changeset :: Changeset.t(t()) |   @type changeset :: Changeset.t(t()) | ||||||
|  |  | ||||||
|   @doc false |   @doc false | ||||||
|   @spec create_changeset(attrs :: map(), User.t()) :: changeset() |   @spec create_changeset(attrs :: map(), User.t()) :: changeset() | ||||||
|   def create_changeset(attrs, %User{id: user_id}) do |   def create_changeset(attrs, %User{id: user_id}) do | ||||||
|     %__MODULE__{} |     %__MODULE__{} | ||||||
|     |> cast(attrs, [:title, :content, :tags, :visibility]) |     |> cast(attrs, [:slug, :content, :tags, :visibility]) | ||||||
|     |> change(user_id: user_id) |     |> change(user_id: user_id) | ||||||
|     |> cast_tags_string(attrs) |     |> 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 |   end | ||||||
|  |  | ||||||
|   @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() |   @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() | ||||||
|   def update_changeset(%{user_id: user_id} = note, attrs, %User{id: user_id}) do |   def update_changeset(%{user_id: user_id} = note, attrs, %User{id: user_id}) do | ||||||
|     note |     note | ||||||
|     |> cast(attrs, [:title, :content, :tags, :visibility]) |     |> cast(attrs, [:slug, :content, :tags, :visibility]) | ||||||
|     |> cast_tags_string(attrs) |     |> 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 |   end | ||||||
|  |  | ||||||
|   defp cast_tags_string(changeset, %{"tags_string" => tags_string}) |   defp cast_tags_string(changeset, %{"tags_string" => tags_string}) | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ defmodule Memex.Notes do | |||||||
|       [%Note{}, ...] |       [%Note{}, ...] | ||||||
|  |  | ||||||
|       iex> list_notes("my note", %User{id: 123}) |       iex> list_notes("my note", %User{id: 123}) | ||||||
|       [%Note{title: "my note"}, ...] |       [%Note{slug: "my note"}, ...] | ||||||
|  |  | ||||||
|   """ |   """ | ||||||
|   @spec list_notes(User.t()) :: [Note.t()] |   @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 \\ nil, user) | ||||||
|  |  | ||||||
|   def list_notes(search, %{id: user_id}) when search |> is_nil() or search == "" do |   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 |   end | ||||||
|  |  | ||||||
|   def list_notes(search, %{id: user_id}) when search |> is_binary() do |   def list_notes(search, %{id: user_id}) when search |> is_binary() do | ||||||
| @@ -57,14 +57,14 @@ defmodule Memex.Notes do | |||||||
|       [%Note{}, ...] |       [%Note{}, ...] | ||||||
|  |  | ||||||
|       iex> list_public_notes("my 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() :: [Note.t()] | ||||||
|   @spec list_public_notes(search :: String.t() | nil) :: [Note.t()] |   @spec list_public_notes(search :: String.t() | nil) :: [Note.t()] | ||||||
|   def list_public_notes(search \\ nil) |   def list_public_notes(search \\ nil) | ||||||
|  |  | ||||||
|   def list_public_notes(search) when search |> is_nil() or search == "" do |   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 |   end | ||||||
|  |  | ||||||
|   def list_public_notes(search) when search |> is_binary() do |   def list_public_notes(search) when search |> is_binary() do | ||||||
| @@ -119,6 +119,37 @@ defmodule Memex.Notes do | |||||||
|     ) |     ) | ||||||
|   end |   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 """ |   @doc """ | ||||||
|   Creates a note. |   Creates a note. | ||||||
|  |  | ||||||
| @@ -189,7 +220,7 @@ defmodule Memex.Notes do | |||||||
|       iex> change_note(note, %User{id: 123}) |       iex> change_note(note, %User{id: 123}) | ||||||
|       %Ecto.Changeset{data: %Note{}} |       %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{}} |       %Ecto.Changeset{data: %Note{}} | ||||||
|  |  | ||||||
|   """ |   """ | ||||||
|   | |||||||
| @@ -4,13 +4,14 @@ defmodule Memex.Notes.Note do | |||||||
|   """ |   """ | ||||||
|   use Ecto.Schema |   use Ecto.Schema | ||||||
|   import Ecto.Changeset |   import Ecto.Changeset | ||||||
|  |   import MemexWeb.Gettext | ||||||
|   alias Ecto.{Changeset, UUID} |   alias Ecto.{Changeset, UUID} | ||||||
|   alias Memex.Accounts.User |   alias Memex.Accounts.User | ||||||
|  |  | ||||||
|   @primary_key {:id, :binary_id, autogenerate: true} |   @primary_key {:id, :binary_id, autogenerate: true} | ||||||
|   @foreign_key_type :binary_id |   @foreign_key_type :binary_id | ||||||
|   schema "notes" do |   schema "notes" do | ||||||
|     field :title, :string |     field :slug, :string | ||||||
|     field :content, :string |     field :content, :string | ||||||
|     field :tags, {:array, :string} |     field :tags, {:array, :string} | ||||||
|     field :tags_string, :string, virtual: true |     field :tags_string, :string, virtual: true | ||||||
| @@ -22,7 +23,7 @@ defmodule Memex.Notes.Note do | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   @type t :: %__MODULE__{ |   @type t :: %__MODULE__{ | ||||||
|           title: String.t(), |           slug: slug(), | ||||||
|           content: String.t(), |           content: String.t(), | ||||||
|           tags: [String.t()] | nil, |           tags: [String.t()] | nil, | ||||||
|           tags_string: String.t(), |           tags_string: String.t(), | ||||||
| @@ -33,24 +34,31 @@ defmodule Memex.Notes.Note do | |||||||
|           updated_at: NaiveDateTime.t() |           updated_at: NaiveDateTime.t() | ||||||
|         } |         } | ||||||
|   @type id :: UUID.t() |   @type id :: UUID.t() | ||||||
|  |   @type slug :: String.t() | ||||||
|   @type changeset :: Changeset.t(t()) |   @type changeset :: Changeset.t(t()) | ||||||
|  |  | ||||||
|   @doc false |   @doc false | ||||||
|   @spec create_changeset(attrs :: map(), User.t()) :: changeset() |   @spec create_changeset(attrs :: map(), User.t()) :: changeset() | ||||||
|   def create_changeset(attrs, %User{id: user_id}) do |   def create_changeset(attrs, %User{id: user_id}) do | ||||||
|     %__MODULE__{} |     %__MODULE__{} | ||||||
|     |> cast(attrs, [:title, :content, :tags, :visibility]) |     |> cast(attrs, [:slug, :content, :tags, :visibility]) | ||||||
|     |> change(user_id: user_id) |     |> change(user_id: user_id) | ||||||
|     |> cast_tags_string(attrs) |     |> 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 |   end | ||||||
|  |  | ||||||
|   @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() |   @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() | ||||||
|   def update_changeset(%{user_id: user_id} = note, attrs, %User{id: user_id}) do |   def update_changeset(%{user_id: user_id} = note, attrs, %User{id: user_id}) do | ||||||
|     note |     note | ||||||
|     |> cast(attrs, [:title, :content, :tags, :visibility]) |     |> cast(attrs, [:slug, :content, :tags, :visibility]) | ||||||
|     |> cast_tags_string(attrs) |     |> 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 |   end | ||||||
|  |  | ||||||
|   defp cast_tags_string(changeset, %{"tags_string" => tags_string}) |   defp cast_tags_string(changeset, %{"tags_string" => tags_string}) | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ defmodule Memex.Pipelines do | |||||||
|       [%Pipeline{}, ...] |       [%Pipeline{}, ...] | ||||||
|  |  | ||||||
|       iex> list_pipelines("my pipeline", %User{id: 123}) |       iex> list_pipelines("my pipeline", %User{id: 123}) | ||||||
|       [%Pipeline{title: "my pipeline"}, ...] |       [%Pipeline{slug: "my pipeline"}, ...] | ||||||
|  |  | ||||||
|   """ |   """ | ||||||
|   @spec list_pipelines(User.t()) :: [Pipeline.t()] |   @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 \\ nil, user) | ||||||
|  |  | ||||||
|   def list_pipelines(search, %{id: user_id}) when search |> is_nil() or search == "" do |   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 |   end | ||||||
|  |  | ||||||
|   def list_pipelines(search, %{id: user_id}) when search |> is_binary() do |   def list_pipelines(search, %{id: user_id}) when search |> is_binary() do | ||||||
| @@ -57,14 +57,14 @@ defmodule Memex.Pipelines do | |||||||
|       [%Pipeline{}, ...] |       [%Pipeline{}, ...] | ||||||
|  |  | ||||||
|       iex> list_public_pipelines("my 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() :: [Pipeline.t()] | ||||||
|   @spec list_public_pipelines(search :: String.t() | nil) :: [Pipeline.t()] |   @spec list_public_pipelines(search :: String.t() | nil) :: [Pipeline.t()] | ||||||
|   def list_public_pipelines(search \\ nil) |   def list_public_pipelines(search \\ nil) | ||||||
|  |  | ||||||
|   def list_public_pipelines(search) when search |> is_nil() or search == "" do |   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 |   end | ||||||
|  |  | ||||||
|   def list_public_pipelines(search) when search |> is_binary() do |   def list_public_pipelines(search) when search |> is_binary() do | ||||||
| @@ -119,6 +119,37 @@ defmodule Memex.Pipelines do | |||||||
|     ) |     ) | ||||||
|   end |   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 """ |   @doc """ | ||||||
|   Creates a pipeline. |   Creates a pipeline. | ||||||
|  |  | ||||||
| @@ -191,7 +222,7 @@ defmodule Memex.Pipelines do | |||||||
|       iex> change_pipeline(pipeline, %User{id: 123}) |       iex> change_pipeline(pipeline, %User{id: 123}) | ||||||
|       %Ecto.Changeset{data: %Pipeline{}} |       %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{}} |       %Ecto.Changeset{data: %Pipeline{}} | ||||||
|  |  | ||||||
|   """ |   """ | ||||||
|   | |||||||
| @@ -4,13 +4,14 @@ defmodule Memex.Pipelines.Pipeline do | |||||||
|   """ |   """ | ||||||
|   use Ecto.Schema |   use Ecto.Schema | ||||||
|   import Ecto.Changeset |   import Ecto.Changeset | ||||||
|  |   import MemexWeb.Gettext | ||||||
|   alias Ecto.{Changeset, UUID} |   alias Ecto.{Changeset, UUID} | ||||||
|   alias Memex.{Accounts.User, Pipelines.Step} |   alias Memex.{Accounts.User, Pipelines.Step} | ||||||
|  |  | ||||||
|   @primary_key {:id, :binary_id, autogenerate: true} |   @primary_key {:id, :binary_id, autogenerate: true} | ||||||
|   @foreign_key_type :binary_id |   @foreign_key_type :binary_id | ||||||
|   schema "pipelines" do |   schema "pipelines" do | ||||||
|     field :title, :string |     field :slug, :string | ||||||
|     field :description, :string |     field :description, :string | ||||||
|     field :tags, {:array, :string} |     field :tags, {:array, :string} | ||||||
|     field :tags_string, :string, virtual: true |     field :tags_string, :string, virtual: true | ||||||
| @@ -24,7 +25,7 @@ defmodule Memex.Pipelines.Pipeline do | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   @type t :: %__MODULE__{ |   @type t :: %__MODULE__{ | ||||||
|           title: String.t(), |           slug: slug(), | ||||||
|           description: String.t(), |           description: String.t(), | ||||||
|           tags: [String.t()] | nil, |           tags: [String.t()] | nil, | ||||||
|           tags_string: String.t(), |           tags_string: String.t(), | ||||||
| @@ -35,24 +36,31 @@ defmodule Memex.Pipelines.Pipeline do | |||||||
|           updated_at: NaiveDateTime.t() |           updated_at: NaiveDateTime.t() | ||||||
|         } |         } | ||||||
|   @type id :: UUID.t() |   @type id :: UUID.t() | ||||||
|  |   @type slug :: String.t() | ||||||
|   @type changeset :: Changeset.t(t()) |   @type changeset :: Changeset.t(t()) | ||||||
|  |  | ||||||
|   @doc false |   @doc false | ||||||
|   @spec create_changeset(attrs :: map(), User.t()) :: changeset() |   @spec create_changeset(attrs :: map(), User.t()) :: changeset() | ||||||
|   def create_changeset(attrs, %User{id: user_id}) do |   def create_changeset(attrs, %User{id: user_id}) do | ||||||
|     %__MODULE__{} |     %__MODULE__{} | ||||||
|     |> cast(attrs, [:title, :description, :tags, :visibility]) |     |> cast(attrs, [:slug, :description, :tags, :visibility]) | ||||||
|     |> change(user_id: user_id) |     |> change(user_id: user_id) | ||||||
|     |> cast_tags_string(attrs) |     |> 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 |   end | ||||||
|  |  | ||||||
|   @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() |   @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() | ||||||
|   def update_changeset(%{user_id: user_id} = pipeline, attrs, %User{id: user_id}) do |   def update_changeset(%{user_id: user_id} = pipeline, attrs, %User{id: user_id}) do | ||||||
|     pipeline |     pipeline | ||||||
|     |> cast(attrs, [:title, :description, :tags, :visibility]) |     |> cast(attrs, [:slug, :description, :tags, :visibility]) | ||||||
|     |> cast_tags_string(attrs) |     |> 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 |   end | ||||||
|  |  | ||||||
|   defp cast_tags_string(changeset, %{"tags_string" => tags_string}) |   defp cast_tags_string(changeset, %{"tags_string" => tags_string}) | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ defmodule MemexWeb.Components.ContextsTableComponent do | |||||||
|       end |       end | ||||||
|  |  | ||||||
|     columns = [ |     columns = [ | ||||||
|       %{label: gettext("title"), key: :title}, |       %{label: gettext("slug"), key: :slug}, | ||||||
|       %{label: gettext("content"), key: :content}, |       %{label: gettext("content"), key: :content}, | ||||||
|       %{label: gettext("tags"), key: :tags}, |       %{label: gettext("tags"), key: :tags}, | ||||||
|       %{label: gettext("visibility"), key: :visibility} |       %{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()) :: |   @spec get_value_for_key(atom(), Context.t(), additional_data :: map()) :: | ||||||
|           any() | {any(), Rendered.t()} |           any() | {any(), Rendered.t()} | ||||||
|   defp get_value_for_key(:title, %{id: id, title: title}, _additional_data) do |   defp get_value_for_key(:slug, %{slug: slug}, _additional_data) do | ||||||
|     assigns = %{id: id, title: title} |     assigns = %{slug: slug} | ||||||
|  |  | ||||||
|     title_block = ~H""" |     slug_block = ~H""" | ||||||
|     <.link |     <.link | ||||||
|       navigate={Routes.context_show_path(Endpoint, :show, @id)} |       navigate={Routes.context_show_path(Endpoint, :show, @slug)} | ||||||
|       class="link" |       class="link" | ||||||
|       data-qa={"context-show-#{@id}"} |       data-qa={"context-show-#{@slug}"} | ||||||
|     > |     > | ||||||
|       <%= @title %> |       <%= @slug %> | ||||||
|     </.link> |     </.link> | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     {title, title_block} |     {slug, slug_block} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp get_value_for_key(:content, %{content: content}, _additional_data) do |   defp get_value_for_key(:content, %{content: content}, _additional_data) do | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ defmodule MemexWeb.Components.NotesTableComponent do | |||||||
|       end |       end | ||||||
|  |  | ||||||
|     columns = [ |     columns = [ | ||||||
|       %{label: gettext("title"), key: :title}, |       %{label: gettext("slug"), key: :slug}, | ||||||
|       %{label: gettext("content"), key: :content}, |       %{label: gettext("content"), key: :content}, | ||||||
|       %{label: gettext("tags"), key: :tags}, |       %{label: gettext("tags"), key: :tags}, | ||||||
|       %{label: gettext("visibility"), key: :visibility} |       %{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()) :: |   @spec get_value_for_key(atom(), Note.t(), additional_data :: map()) :: | ||||||
|           any() | {any(), Rendered.t()} |           any() | {any(), Rendered.t()} | ||||||
|   defp get_value_for_key(:title, %{id: id, title: title}, _additional_data) do |   defp get_value_for_key(:slug, %{slug: slug}, _additional_data) do | ||||||
|     assigns = %{id: id, title: title} |     assigns = %{slug: slug} | ||||||
|  |  | ||||||
|     title_block = ~H""" |     slug_block = ~H""" | ||||||
|     <.link |     <.link | ||||||
|       navigate={Routes.note_show_path(Endpoint, :show, @id)} |       navigate={Routes.note_show_path(Endpoint, :show, @slug)} | ||||||
|       class="link" |       class="link" | ||||||
|       data-qa={"note-show-#{@id}"} |       data-qa={"note-show-#{@slug}"} | ||||||
|     > |     > | ||||||
|       <%= @title %> |       <%= @slug %> | ||||||
|     </.link> |     </.link> | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     {title, title_block} |     {slug, slug_block} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp get_value_for_key(:content, %{content: content}, _additional_data) do |   defp get_value_for_key(:content, %{content: content}, _additional_data) do | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ defmodule MemexWeb.Components.PipelinesTableComponent do | |||||||
|       end |       end | ||||||
|  |  | ||||||
|     columns = [ |     columns = [ | ||||||
|       %{label: gettext("title"), key: :title}, |       %{label: gettext("slug"), key: :slug}, | ||||||
|       %{label: gettext("description"), key: :description}, |       %{label: gettext("description"), key: :description}, | ||||||
|       %{label: gettext("tags"), key: :tags}, |       %{label: gettext("tags"), key: :tags}, | ||||||
|       %{label: gettext("visibility"), key: :visibility} |       %{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()) :: |   @spec get_value_for_key(atom(), Pipeline.t(), additional_data :: map()) :: | ||||||
|           any() | {any(), Rendered.t()} |           any() | {any(), Rendered.t()} | ||||||
|   defp get_value_for_key(:title, %{id: id, title: title}, _additional_data) do |   defp get_value_for_key(:slug, %{slug: slug}, _additional_data) do | ||||||
|     assigns = %{id: id, title: title} |     assigns = %{slug: slug} | ||||||
|  |  | ||||||
|     title_block = ~H""" |     slug_block = ~H""" | ||||||
|     <.link |     <.link | ||||||
|       navigate={Routes.pipeline_show_path(Endpoint, :show, @id)} |       navigate={Routes.pipeline_show_path(Endpoint, :show, @slug)} | ||||||
|       class="link" |       class="link" | ||||||
|       data-qa={"pipeline-show-#{@id}"} |       data-qa={"pipeline-show-#{@slug}"} | ||||||
|     > |     > | ||||||
|       <%= @title %> |       <%= @slug %> | ||||||
|     </.link> |     </.link> | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     {title, title_block} |     {slug, slug_block} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp get_value_for_key(:description, %{description: description}, _additional_data) do |   defp get_value_for_key(:description, %{description: description}, _additional_data) do | ||||||
|   | |||||||
| @@ -38,10 +38,10 @@ defmodule MemexWeb.ContextLive.FormComponent do | |||||||
|          context_params |          context_params | ||||||
|        ) do |        ) do | ||||||
|     case Contexts.update_context(context, context_params, current_user) do |     case Contexts.update_context(context, context_params, current_user) do | ||||||
|       {:ok, %{title: title}} -> |       {:ok, %{slug: slug}} -> | ||||||
|         {:noreply, |         {:noreply, | ||||||
|          socket |          socket | ||||||
|          |> put_flash(:info, gettext("%{title} saved", title: title)) |          |> put_flash(:info, gettext("%{slug} saved", slug: slug)) | ||||||
|          |> push_navigate(to: return_to)} |          |> push_navigate(to: return_to)} | ||||||
|  |  | ||||||
|       {:error, %Ecto.Changeset{} = changeset} -> |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
| @@ -55,10 +55,10 @@ defmodule MemexWeb.ContextLive.FormComponent do | |||||||
|          context_params |          context_params | ||||||
|        ) do |        ) do | ||||||
|     case Contexts.create_context(context_params, current_user) do |     case Contexts.create_context(context_params, current_user) do | ||||||
|       {:ok, %{title: title}} -> |       {:ok, %{slug: slug}} -> | ||||||
|         {:noreply, |         {:noreply, | ||||||
|          socket |          socket | ||||||
|          |> put_flash(:info, gettext("%{title} created", title: title)) |          |> put_flash(:info, gettext("%{slug} created", slug: slug)) | ||||||
|          |> push_navigate(to: return_to)} |          |> push_navigate(to: return_to)} | ||||||
|  |  | ||||||
|       {:error, %Ecto.Changeset{} = changeset} -> |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
|   | |||||||
| @@ -9,11 +9,11 @@ | |||||||
|     phx-debounce="300" |     phx-debounce="300" | ||||||
|     class="flex flex-col justify-start items-stretch space-y-4" |     class="flex flex-col justify-start items-stretch space-y-4" | ||||||
|   > |   > | ||||||
|     <%= text_input(f, :title, |     <%= text_input(f, :slug, | ||||||
|       class: "input input-primary", |       class: "input input-primary", | ||||||
|       placeholder: gettext("title") |       placeholder: gettext("slug") | ||||||
|     ) %> |     ) %> | ||||||
|     <%= error_tag(f, :title) %> |     <%= error_tag(f, :slug) %> | ||||||
|  |  | ||||||
|     <%= textarea(f, :content, |     <%= textarea(f, :content, | ||||||
|       id: "context-form-content", |       id: "context-form-content", | ||||||
|   | |||||||
| @@ -16,11 +16,11 @@ defmodule MemexWeb.ContextLive.Index do | |||||||
|     {:noreply, apply_action(socket, live_action, params)} |     {:noreply, apply_action(socket, live_action, params)} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do |   defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"slug" => slug}) do | ||||||
|     %{title: title} = context = Contexts.get_context!(id, current_user) |     %{slug: slug} = context = Contexts.get_context_by_slug(slug, current_user) | ||||||
|  |  | ||||||
|     socket |     socket | ||||||
|     |> assign(page_title: gettext("edit %{title}", title: title)) |     |> assign(page_title: gettext("edit %{slug}", slug: slug)) | ||||||
|     |> assign(context: context) |     |> assign(context: context) | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -49,12 +49,12 @@ defmodule MemexWeb.ContextLive.Index do | |||||||
|   @impl true |   @impl true | ||||||
|   def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do |   def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do | ||||||
|     context = Contexts.get_context!(id, current_user) |     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 = | ||||||
|       socket |       socket | ||||||
|       |> assign(contexts: Contexts.list_contexts(current_user)) |       |> 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} |     {:noreply, socket} | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ | |||||||
|       <:actions :let={context}> |       <:actions :let={context}> | ||||||
|         <%= if is_owner?(context, @current_user) do %> |         <%= if is_owner?(context, @current_user) do %> | ||||||
|           <.link |           <.link | ||||||
|             patch={Routes.context_index_path(@socket, :edit, context)} |             patch={Routes.context_index_path(@socket, :edit, context.slug)} | ||||||
|             data-qa={"context-edit-#{context.id}"} |             data-qa={"context-edit-#{context.id}"} | ||||||
|           > |           > | ||||||
|             <%= dgettext("actions", "edit") %> |             <%= dgettext("actions", "edit") %> | ||||||
|   | |||||||
| @@ -10,11 +10,15 @@ defmodule MemexWeb.ContextLive.Show do | |||||||
|  |  | ||||||
|   @impl true |   @impl true | ||||||
|   def handle_params( |   def handle_params( | ||||||
|         %{"id" => id}, |         %{"slug" => slug}, | ||||||
|         _, |         _, | ||||||
|         %{assigns: %{live_action: live_action, current_user: current_user}} = socket |         %{assigns: %{live_action: live_action, current_user: current_user}} = socket | ||||||
|       ) do |       ) 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 = | ||||||
|       socket |       socket | ||||||
| @@ -30,18 +34,18 @@ defmodule MemexWeb.ContextLive.Show do | |||||||
|         _params, |         _params, | ||||||
|         %{assigns: %{context: context, current_user: current_user}} = socket |         %{assigns: %{context: context, current_user: current_user}} = socket | ||||||
|       ) do |       ) do | ||||||
|     {:ok, %{title: title}} = Contexts.delete_context(context, current_user) |     {:ok, %{slug: slug}} = Contexts.delete_context(context, current_user) | ||||||
|  |  | ||||||
|     socket = |     socket = | ||||||
|       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)) |       |> push_navigate(to: Routes.context_index_path(Endpoint, :index)) | ||||||
|  |  | ||||||
|     {:noreply, socket} |     {:noreply, socket} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp page_title(:show, %{title: title}), do: title |   defp page_title(:show, %{slug: slug}), do: slug | ||||||
|   defp page_title(:edit, %{title: title}), do: gettext("edit %{title}", title: title) |   defp page_title(:edit, %{slug: slug}), do: gettext("edit %{slug}", slug: slug) | ||||||
|  |  | ||||||
|   @spec is_owner_or_admin?(Context.t(), User.t()) :: boolean() |   @spec is_owner_or_admin?(Context.t(), User.t()) :: boolean() | ||||||
|   defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true |   defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <div class="mx-auto flex flex-col justify-center items-stretch space-y-4 max-w-3xl"> | <div class="mx-auto flex flex-col justify-center items-stretch space-y-4 max-w-3xl"> | ||||||
|   <h1 class="text-xl"> |   <h1 class="text-xl"> | ||||||
|     <%= @context.title %> |     <%= @context.slug %> | ||||||
|   </h1> |   </h1> | ||||||
|  |  | ||||||
|   <p><%= if @context.tags, do: @context.tags |> Enum.join(", ") %></p> |   <p><%= if @context.tags, do: @context.tags |> Enum.join(", ") %></p> | ||||||
| @@ -23,7 +23,10 @@ | |||||||
|       <%= dgettext("actions", "back") %> |       <%= dgettext("actions", "back") %> | ||||||
|     </.link> |     </.link> | ||||||
|     <%= if is_owner?(@context, @current_user) do %> |     <%= 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") %> |         <%= dgettext("actions", "edit") %> | ||||||
|       </.link> |       </.link> | ||||||
|     <% end %> |     <% end %> | ||||||
| @@ -42,7 +45,7 @@ | |||||||
| </div> | </div> | ||||||
|  |  | ||||||
| <%= if @live_action in [:edit] do %> | <%= 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 |     <.live_component | ||||||
|       module={MemexWeb.ContextLive.FormComponent} |       module={MemexWeb.ContextLive.FormComponent} | ||||||
|       id={@context.id} |       id={@context.id} | ||||||
| @@ -50,7 +53,7 @@ | |||||||
|       title={@page_title} |       title={@page_title} | ||||||
|       action={@live_action} |       action={@live_action} | ||||||
|       context={@context} |       context={@context} | ||||||
|       return_to={Routes.context_show_path(@socket, :show, @context)} |       return_to={Routes.context_show_path(@socket, :show, @context.slug)} | ||||||
|     /> |     /> | ||||||
|   </.modal> |   </.modal> | ||||||
| <% end %> | <% end %> | ||||||
|   | |||||||
| @@ -37,10 +37,10 @@ defmodule MemexWeb.NoteLive.FormComponent do | |||||||
|          note_params |          note_params | ||||||
|        ) do |        ) do | ||||||
|     case Notes.update_note(note, note_params, current_user) do |     case Notes.update_note(note, note_params, current_user) do | ||||||
|       {:ok, %{title: title}} -> |       {:ok, %{slug: slug}} -> | ||||||
|         {:noreply, |         {:noreply, | ||||||
|          socket |          socket | ||||||
|          |> put_flash(:info, gettext("%{title} saved", title: title)) |          |> put_flash(:info, gettext("%{slug} saved", slug: slug)) | ||||||
|          |> push_navigate(to: return_to)} |          |> push_navigate(to: return_to)} | ||||||
|  |  | ||||||
|       {:error, %Ecto.Changeset{} = changeset} -> |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
| @@ -54,10 +54,10 @@ defmodule MemexWeb.NoteLive.FormComponent do | |||||||
|          note_params |          note_params | ||||||
|        ) do |        ) do | ||||||
|     case Notes.create_note(note_params, current_user) do |     case Notes.create_note(note_params, current_user) do | ||||||
|       {:ok, %{title: title}} -> |       {:ok, %{slug: slug}} -> | ||||||
|         {:noreply, |         {:noreply, | ||||||
|          socket |          socket | ||||||
|          |> put_flash(:info, gettext("%{title} created", title: title)) |          |> put_flash(:info, gettext("%{slug} created", slug: slug)) | ||||||
|          |> push_navigate(to: return_to)} |          |> push_navigate(to: return_to)} | ||||||
|  |  | ||||||
|       {:error, %Ecto.Changeset{} = changeset} -> |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
|   | |||||||
| @@ -9,11 +9,11 @@ | |||||||
|     phx-debounce="300" |     phx-debounce="300" | ||||||
|     class="flex flex-col justify-start items-stretch space-y-4" |     class="flex flex-col justify-start items-stretch space-y-4" | ||||||
|   > |   > | ||||||
|     <%= text_input(f, :title, |     <%= text_input(f, :slug, | ||||||
|       class: "input input-primary", |       class: "input input-primary", | ||||||
|       placeholder: gettext("title") |       placeholder: gettext("slug") | ||||||
|     ) %> |     ) %> | ||||||
|     <%= error_tag(f, :title) %> |     <%= error_tag(f, :slug) %> | ||||||
|  |  | ||||||
|     <%= textarea(f, :content, |     <%= textarea(f, :content, | ||||||
|       id: "note-form-content", |       id: "note-form-content", | ||||||
|   | |||||||
| @@ -16,11 +16,11 @@ defmodule MemexWeb.NoteLive.Index do | |||||||
|     {:noreply, apply_action(socket, live_action, params)} |     {:noreply, apply_action(socket, live_action, params)} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do |   defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"slug" => slug}) do | ||||||
|     %{title: title} = note = Notes.get_note!(id, current_user) |     %{slug: slug} = note = Notes.get_note_by_slug(slug, current_user) | ||||||
|  |  | ||||||
|     socket |     socket | ||||||
|     |> assign(page_title: gettext("edit %{title}", title: title)) |     |> assign(page_title: gettext("edit %{slug}", slug: slug)) | ||||||
|     |> assign(note: note) |     |> assign(note: note) | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -49,12 +49,12 @@ defmodule MemexWeb.NoteLive.Index do | |||||||
|   @impl true |   @impl true | ||||||
|   def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do |   def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do | ||||||
|     note = Notes.get_note!(id, current_user) |     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 = | ||||||
|       socket |       socket | ||||||
|       |> assign(notes: Notes.list_notes(current_user)) |       |> 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} |     {:noreply, socket} | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ | |||||||
|       <:actions :let={note}> |       <:actions :let={note}> | ||||||
|         <%= if is_owner?(note, @current_user) do %> |         <%= if is_owner?(note, @current_user) do %> | ||||||
|           <.link |           <.link | ||||||
|             patch={Routes.note_index_path(@socket, :edit, note)} |             patch={Routes.note_index_path(@socket, :edit, note.slug)} | ||||||
|             data-qa={"note-edit-#{note.id}"} |             data-qa={"note-edit-#{note.id}"} | ||||||
|           > |           > | ||||||
|             <%= dgettext("actions", "edit") %> |             <%= dgettext("actions", "edit") %> | ||||||
|   | |||||||
| @@ -10,11 +10,15 @@ defmodule MemexWeb.NoteLive.Show do | |||||||
|  |  | ||||||
|   @impl true |   @impl true | ||||||
|   def handle_params( |   def handle_params( | ||||||
|         %{"id" => id}, |         %{"slug" => slug}, | ||||||
|         _, |         _, | ||||||
|         %{assigns: %{live_action: live_action, current_user: current_user}} = socket |         %{assigns: %{live_action: live_action, current_user: current_user}} = socket | ||||||
|       ) do |       ) 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 = | ||||||
|       socket |       socket | ||||||
| @@ -30,18 +34,18 @@ defmodule MemexWeb.NoteLive.Show do | |||||||
|         _params, |         _params, | ||||||
|         %{assigns: %{note: note, current_user: current_user}} = socket |         %{assigns: %{note: note, current_user: current_user}} = socket | ||||||
|       ) do |       ) do | ||||||
|     {:ok, %{title: title}} = Notes.delete_note(note, current_user) |     {:ok, %{slug: slug}} = Notes.delete_note(note, current_user) | ||||||
|  |  | ||||||
|     socket = |     socket = | ||||||
|       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)) |       |> push_navigate(to: Routes.note_index_path(Endpoint, :index)) | ||||||
|  |  | ||||||
|     {:noreply, socket} |     {:noreply, socket} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp page_title(:show, %{title: title}), do: title |   defp page_title(:show, %{slug: slug}), do: slug | ||||||
|   defp page_title(:edit, %{title: title}), do: gettext("edit %{title}", title: title) |   defp page_title(:edit, %{slug: slug}), do: gettext("edit %{slug}", slug: slug) | ||||||
|  |  | ||||||
|   @spec is_owner_or_admin?(Note.t(), User.t()) :: boolean() |   @spec is_owner_or_admin?(Note.t(), User.t()) :: boolean() | ||||||
|   defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true |   defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <div class="mx-auto flex flex-col justify-center items-stretch space-y-4 max-w-3xl"> | <div class="mx-auto flex flex-col justify-center items-stretch space-y-4 max-w-3xl"> | ||||||
|   <h1 class="text-xl"> |   <h1 class="text-xl"> | ||||||
|     <%= @note.title %> |     <%= @note.slug %> | ||||||
|   </h1> |   </h1> | ||||||
|  |  | ||||||
|   <p><%= if @note.tags, do: @note.tags |> Enum.join(", ") %></p> |   <p><%= if @note.tags, do: @note.tags |> Enum.join(", ") %></p> | ||||||
| @@ -23,7 +23,7 @@ | |||||||
|       <%= dgettext("actions", "back") %> |       <%= dgettext("actions", "back") %> | ||||||
|     </.link> |     </.link> | ||||||
|     <%= if is_owner?(@note, @current_user) do %> |     <%= 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") %> |         <%= dgettext("actions", "edit") %> | ||||||
|       </.link> |       </.link> | ||||||
|     <% end %> |     <% end %> | ||||||
| @@ -42,7 +42,7 @@ | |||||||
| </div> | </div> | ||||||
|  |  | ||||||
| <%= if @live_action in [:edit] do %> | <%= 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 |     <.live_component | ||||||
|       module={MemexWeb.NoteLive.FormComponent} |       module={MemexWeb.NoteLive.FormComponent} | ||||||
|       id={@note.id} |       id={@note.id} | ||||||
| @@ -50,7 +50,7 @@ | |||||||
|       title={@page_title} |       title={@page_title} | ||||||
|       action={@live_action} |       action={@live_action} | ||||||
|       note={@note} |       note={@note} | ||||||
|       return_to={Routes.note_show_path(@socket, :show, @note)} |       return_to={Routes.note_show_path(@socket, :show, @note.slug)} | ||||||
|     /> |     /> | ||||||
|   </.modal> |   </.modal> | ||||||
| <% end %> | <% end %> | ||||||
|   | |||||||
| @@ -42,10 +42,10 @@ defmodule MemexWeb.PipelineLive.FormComponent do | |||||||
|          pipeline_params |          pipeline_params | ||||||
|        ) do |        ) do | ||||||
|     case Pipelines.update_pipeline(pipeline, pipeline_params, current_user) do |     case Pipelines.update_pipeline(pipeline, pipeline_params, current_user) do | ||||||
|       {:ok, %{title: title}} -> |       {:ok, %{slug: slug}} -> | ||||||
|         {:noreply, |         {:noreply, | ||||||
|          socket |          socket | ||||||
|          |> put_flash(:info, gettext("%{title} saved", title: title)) |          |> put_flash(:info, gettext("%{slug} saved", slug: slug)) | ||||||
|          |> push_navigate(to: return_to)} |          |> push_navigate(to: return_to)} | ||||||
|  |  | ||||||
|       {:error, %Ecto.Changeset{} = changeset} -> |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
| @@ -59,10 +59,10 @@ defmodule MemexWeb.PipelineLive.FormComponent do | |||||||
|          pipeline_params |          pipeline_params | ||||||
|        ) do |        ) do | ||||||
|     case Pipelines.create_pipeline(pipeline_params, current_user) do |     case Pipelines.create_pipeline(pipeline_params, current_user) do | ||||||
|       {:ok, %{title: title}} -> |       {:ok, %{slug: slug}} -> | ||||||
|         {:noreply, |         {:noreply, | ||||||
|          socket |          socket | ||||||
|          |> put_flash(:info, gettext("%{title} created", title: title)) |          |> put_flash(:info, gettext("%{slug} created", slug: slug)) | ||||||
|          |> push_navigate(to: return_to)} |          |> push_navigate(to: return_to)} | ||||||
|  |  | ||||||
|       {:error, %Ecto.Changeset{} = changeset} -> |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
|   | |||||||
| @@ -9,11 +9,11 @@ | |||||||
|     phx-debounce="300" |     phx-debounce="300" | ||||||
|     class="flex flex-col justify-start items-stretch space-y-4" |     class="flex flex-col justify-start items-stretch space-y-4" | ||||||
|   > |   > | ||||||
|     <%= text_input(f, :title, |     <%= text_input(f, :slug, | ||||||
|       class: "input input-primary", |       class: "input input-primary", | ||||||
|       placeholder: gettext("title") |       placeholder: gettext("slug") | ||||||
|     ) %> |     ) %> | ||||||
|     <%= error_tag(f, :title) %> |     <%= error_tag(f, :slug) %> | ||||||
|  |  | ||||||
|     <%= textarea(f, :description, |     <%= textarea(f, :description, | ||||||
|       id: "pipeline-form-description", |       id: "pipeline-form-description", | ||||||
|   | |||||||
| @@ -16,11 +16,11 @@ defmodule MemexWeb.PipelineLive.Index do | |||||||
|     {:noreply, apply_action(socket, live_action, params)} |     {:noreply, apply_action(socket, live_action, params)} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do |   defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"slug" => slug}) do | ||||||
|     %{title: title} = pipeline = Pipelines.get_pipeline!(id, current_user) |     %{slug: slug} = pipeline = Pipelines.get_pipeline_by_slug(slug, current_user) | ||||||
|  |  | ||||||
|     socket |     socket | ||||||
|     |> assign(page_title: gettext("edit %{title}", title: title)) |     |> assign(page_title: gettext("edit %{slug}", slug: slug)) | ||||||
|     |> assign(pipeline: pipeline) |     |> assign(pipeline: pipeline) | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -49,12 +49,12 @@ defmodule MemexWeb.PipelineLive.Index do | |||||||
|   @impl true |   @impl true | ||||||
|   def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do |   def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do | ||||||
|     pipeline = Pipelines.get_pipeline!(id, current_user) |     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 = | ||||||
|       socket |       socket | ||||||
|       |> assign(pipelines: Pipelines.list_pipelines(current_user)) |       |> 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} |     {:noreply, socket} | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ | |||||||
|       <:actions :let={pipeline}> |       <:actions :let={pipeline}> | ||||||
|         <%= if is_owner?(pipeline, @current_user) do %> |         <%= if is_owner?(pipeline, @current_user) do %> | ||||||
|           <.link |           <.link | ||||||
|             patch={Routes.pipeline_index_path(@socket, :edit, pipeline)} |             patch={Routes.pipeline_index_path(@socket, :edit, pipeline.slug)} | ||||||
|             data-qa={"pipeline-edit-#{pipeline.id}"} |             data-qa={"pipeline-edit-#{pipeline.id}"} | ||||||
|           > |           > | ||||||
|             <%= dgettext("actions", "edit") %> |             <%= dgettext("actions", "edit") %> | ||||||
|   | |||||||
| @@ -10,11 +10,15 @@ defmodule MemexWeb.PipelineLive.Show do | |||||||
|  |  | ||||||
|   @impl true |   @impl true | ||||||
|   def handle_params( |   def handle_params( | ||||||
|         %{"id" => id}, |         %{"slug" => slug}, | ||||||
|         _, |         _, | ||||||
|         %{assigns: %{live_action: live_action, current_user: current_user}} = socket |         %{assigns: %{live_action: live_action, current_user: current_user}} = socket | ||||||
|       ) do |       ) 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 = | ||||||
|       socket |       socket | ||||||
| @@ -30,18 +34,18 @@ defmodule MemexWeb.PipelineLive.Show do | |||||||
|         _params, |         _params, | ||||||
|         %{assigns: %{pipeline: pipeline, current_user: current_user}} = socket |         %{assigns: %{pipeline: pipeline, current_user: current_user}} = socket | ||||||
|       ) do |       ) do | ||||||
|     {:ok, %{title: title}} = Pipelines.delete_pipeline(pipeline, current_user) |     {:ok, %{slug: slug}} = Pipelines.delete_pipeline(pipeline, current_user) | ||||||
|  |  | ||||||
|     socket = |     socket = | ||||||
|       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)) |       |> push_navigate(to: Routes.pipeline_index_path(Endpoint, :index)) | ||||||
|  |  | ||||||
|     {:noreply, socket} |     {:noreply, socket} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   defp page_title(:show, %{title: title}), do: title |   defp page_title(:show, %{slug: slug}), do: slug | ||||||
|   defp page_title(:edit, %{title: title}), do: gettext("edit %{title}", title: title) |   defp page_title(:edit, %{slug: slug}), do: gettext("edit %{slug}", slug: slug) | ||||||
|  |  | ||||||
|   @spec is_owner_or_admin?(Pipeline.t(), User.t()) :: boolean() |   @spec is_owner_or_admin?(Pipeline.t(), User.t()) :: boolean() | ||||||
|   defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true |   defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <div class="mx-auto flex flex-col justify-center items-stretch space-y-4 max-w-3xl"> | <div class="mx-auto flex flex-col justify-center items-stretch space-y-4 max-w-3xl"> | ||||||
|   <h1 class="text-xl"> |   <h1 class="text-xl"> | ||||||
|     <%= @pipeline.title %> |     <%= @pipeline.slug %> | ||||||
|   </h1> |   </h1> | ||||||
|  |  | ||||||
|   <p><%= if @pipeline.tags, do: @pipeline.tags |> Enum.join(", ") %></p> |   <p><%= if @pipeline.tags, do: @pipeline.tags |> Enum.join(", ") %></p> | ||||||
| @@ -23,7 +23,10 @@ | |||||||
|       <%= dgettext("actions", "back") %> |       <%= dgettext("actions", "back") %> | ||||||
|     </.link> |     </.link> | ||||||
|     <%= if is_owner?(@pipeline, @current_user) do %> |     <%= 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") %> |         <%= dgettext("actions", "edit") %> | ||||||
|       </.link> |       </.link> | ||||||
|     <% end %> |     <% end %> | ||||||
| @@ -42,7 +45,7 @@ | |||||||
| </div> | </div> | ||||||
|  |  | ||||||
| <%= if @live_action in [:edit] do %> | <%= 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 |     <.live_component | ||||||
|       module={MemexWeb.PipelineLive.FormComponent} |       module={MemexWeb.PipelineLive.FormComponent} | ||||||
|       id={@pipeline.id} |       id={@pipeline.id} | ||||||
| @@ -50,7 +53,7 @@ | |||||||
|       title={@page_title} |       title={@page_title} | ||||||
|       action={@live_action} |       action={@live_action} | ||||||
|       pipeline={@pipeline} |       pipeline={@pipeline} | ||||||
|       return_to={Routes.pipeline_show_path(@socket, :show, @pipeline)} |       return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)} | ||||||
|     /> |     /> | ||||||
|   </.modal> |   </.modal> | ||||||
| <% end %> | <% end %> | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								lib/memex_web/not_found_error.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/memex_web/not_found_error.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | defmodule MemexWeb.NotFoundError do | ||||||
|  |   defexception [:message, plug_status: 404] | ||||||
|  | end | ||||||
| @@ -58,16 +58,16 @@ defmodule MemexWeb.Router do | |||||||
|       pipe_through [:browser, :require_authenticated_user] |       pipe_through [:browser, :require_authenticated_user] | ||||||
|  |  | ||||||
|       live "/notes/new", NoteLive.Index, :new |       live "/notes/new", NoteLive.Index, :new | ||||||
|       live "/notes/:id/edit", NoteLive.Index, :edit |       live "/notes/:slug/edit", NoteLive.Index, :edit | ||||||
|       live "/note/:id/edit", NoteLive.Show, :edit |       live "/note/:slug/edit", NoteLive.Show, :edit | ||||||
|  |  | ||||||
|       live "/contexts/new", ContextLive.Index, :new |       live "/contexts/new", ContextLive.Index, :new | ||||||
|       live "/contexts/:id/edit", ContextLive.Index, :edit |       live "/contexts/:slug/edit", ContextLive.Index, :edit | ||||||
|       live "/context/:id/edit", ContextLive.Show, :edit |       live "/context/:slug/edit", ContextLive.Show, :edit | ||||||
|  |  | ||||||
|       live "/pipelines/new", PipelineLive.Index, :new |       live "/pipelines/new", PipelineLive.Index, :new | ||||||
|       live "/pipelines/:id/edit", PipelineLive.Index, :edit |       live "/pipelines/:slug/edit", PipelineLive.Index, :edit | ||||||
|       live "/pipelines/:id/show/edit", PipelineLive.Show, :edit |       live "/pipeline/:slug/edit", PipelineLive.Show, :edit | ||||||
|  |  | ||||||
|       get "/users/settings", UserSettingsController, :edit |       get "/users/settings", UserSettingsController, :edit | ||||||
|       put "/users/settings", UserSettingsController, :update |       put "/users/settings", UserSettingsController, :update | ||||||
| @@ -80,15 +80,15 @@ defmodule MemexWeb.Router do | |||||||
|  |  | ||||||
|       live "/notes", NoteLive.Index, :index |       live "/notes", NoteLive.Index, :index | ||||||
|       live "/notes/:search", NoteLive.Index, :search |       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", ContextLive.Index, :index | ||||||
|       live "/contexts/:search", ContextLive.Index, :search |       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", PipelineLive.Index, :index | ||||||
|       live "/pipelines/:search", PipelineLive.Index, :search |       live "/pipelines/:search", PipelineLive.Index, :search | ||||||
|       live "/pipeline/:id", PipelineLive.Show, :show |       live "/pipeline/:slug", PipelineLive.Show, :show | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|     <script defer type="text/javascript" src="/js/app.js"> |     <script defer type="text/javascript" src="/js/app.js"> | ||||||
|     </script> |     </script> | ||||||
|   </head> |   </head> | ||||||
|   <body class="pb-8 m-0 p-0 w-full h-full"> |   <body class="m-0 p-0 w-full h-full bg-primary-800 text-primary-400 subpixel-antialiased"> | ||||||
|     <header> |     <header> | ||||||
|       <.topbar current_user={assigns[:current_user]}></.topbar> |       <.topbar current_user={assigns[:current_user]}></.topbar> | ||||||
|     </header> |     </header> | ||||||
| @@ -25,7 +25,7 @@ | |||||||
|         <hr class="w-full hr" /> |         <hr class="w-full hr" /> | ||||||
|  |  | ||||||
|         <a href={Routes.live_path(Endpoint, HomeLive)} class="link title text-primary-400 text-lg"> |         <a href={Routes.live_path(Endpoint, HomeLive)} class="link title text-primary-400 text-lg"> | ||||||
|           <%= dgettext("errors", "Go back home") %> |           <%= dgettext("errors", "go back home") %> | ||||||
|         </a> |         </a> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -6,9 +6,9 @@ defmodule MemexWeb.ErrorView do | |||||||
|   def template_not_found(error_path, _assigns) do |   def template_not_found(error_path, _assigns) do | ||||||
|     error_string = |     error_string = | ||||||
|       case error_path do |       case error_path do | ||||||
|         "404.html" -> dgettext("errors", "Not found") |         "404.html" -> dgettext("errors", "not found") | ||||||
|         "401.html" -> dgettext("errors", "Unauthorized") |         "401.html" -> dgettext("errors", "unauthorized") | ||||||
|         _ -> dgettext("errors", "Internal Server Error") |         _ -> dgettext("errors", "internal server error") | ||||||
|       end |       end | ||||||
|  |  | ||||||
|     render("error.html", %{error_string: error_string}) |     render("error.html", %{error_string: error_string}) | ||||||
|   | |||||||
| @@ -68,11 +68,11 @@ msgid "create invite" | |||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: lib/memex_web/live/context_live/index.html.heex:49 | #: 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/index.html.heex:49 | ||||||
| #: lib/memex_web/live/note_live/show.html.heex:38 | #: 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/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 | #, elixir-autogen, elixir-format | ||||||
| msgid "delete" | msgid "delete" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -83,11 +83,11 @@ msgid "delete user" | |||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: lib/memex_web/live/context_live/index.html.heex:38 | #: 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/index.html.heex:38 | ||||||
| #: lib/memex_web/live/note_live/show.html.heex:27 | #: 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/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 | #, elixir-autogen, elixir-format | ||||||
| msgid "edit" | msgid "edit" | ||||||
| msgstr "" | msgstr "" | ||||||
|   | |||||||
| @@ -10,30 +10,6 @@ | |||||||
| msgid "" | msgid "" | ||||||
| msgstr "" | 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 | #: lib/memex_web/live/invite_live/index.html.heex:90 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "Admins" | msgid "Admins" | ||||||
| @@ -183,16 +159,6 @@ msgstr "" | |||||||
| msgid "document your processes, attaching contexts to each step" | msgid "document your processes, attaching contexts to each step" | ||||||
| msgstr "" | 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 | #: lib/memex_web/live/invite_live/index.ex:33 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "edit invite" | msgid "edit invite" | ||||||
| @@ -392,16 +358,6 @@ msgstr "" | |||||||
| msgid "tags" | msgid "tags" | ||||||
| msgstr "" | 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 | #: lib/memex_web/components/invite_card.ex:20 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "unlimited" | msgid "unlimited" | ||||||
| @@ -476,3 +432,54 @@ msgstr "" | |||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "no pipelines found" | msgid "no pipelines found" | ||||||
| msgstr "" | 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 "" | ||||||
|   | |||||||
| @@ -20,26 +20,11 @@ msgstr "" | |||||||
| msgid "Error" | msgid "Error" | ||||||
| msgstr "" | 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 | #: lib/memex_web/controllers/user_session_controller.ex:17 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "Invalid email or password" | msgid "Invalid email or password" | ||||||
| msgstr "" | 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_registration/new.html.heex:15 | ||||||
| #: lib/memex_web/templates/user_reset_password/edit.html.heex:15 | #: lib/memex_web/templates/user_reset_password/edit.html.heex:15 | ||||||
| #: lib/memex_web/templates/user_settings/edit.html.heex:64 | #: lib/memex_web/templates/user_settings/edit.html.heex:64 | ||||||
| @@ -69,11 +54,6 @@ msgstr "" | |||||||
| msgid "Unable to delete user" | msgid "Unable to delete user" | ||||||
| msgstr "" | 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 | #: lib/memex_web/controllers/user_confirmation_controller.ex:54 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "User confirmation link is invalid or it has expired." | msgid "User confirmation link is invalid or it has expired." | ||||||
| @@ -120,3 +100,33 @@ msgstr "" | |||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "oops, something went wrong! Please check the errors below" | msgid "oops, something went wrong! Please check the errors below" | ||||||
| msgstr "" | 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 "" | ||||||
|   | |||||||
| @@ -142,11 +142,11 @@ msgid "are you sure you want to make %{invite_name} unlimited?" | |||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: lib/memex_web/live/context_live/index.html.heex:46 | #: 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/index.html.heex:46 | ||||||
| #: lib/memex_web/live/note_live/show.html.heex:35 | #: 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/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 | #, elixir-autogen, elixir-format | ||||||
| msgid "are you sure?" | msgid "are you sure?" | ||||||
| msgstr "" | msgstr "" | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								priv/repo/migrations/20221126182210_use_slugs.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								priv/repo/migrations/20221126182210_use_slugs.exs
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -3,7 +3,7 @@ defmodule Memex.ContextsTest do | |||||||
|   import Memex.ContextsFixtures |   import Memex.ContextsFixtures | ||||||
|   alias Memex.{Contexts, Contexts.Context} |   alias Memex.{Contexts, Contexts.Context} | ||||||
|   @moduletag :contexts_test |   @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 |   describe "contexts" do | ||||||
|     setup do |     setup do | ||||||
| @@ -11,9 +11,9 @@ defmodule Memex.ContextsTest do | |||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "list_contexts/1 returns all contexts for a user", %{user: user} do |     test "list_contexts/1 returns all contexts for a user", %{user: user} do | ||||||
|       context_a = context_fixture(%{title: "a", visibility: :public}, user) |       context_a = context_fixture(%{slug: "a", visibility: :public}, user) | ||||||
|       context_b = context_fixture(%{title: "b", visibility: :unlisted}, user) |       context_b = context_fixture(%{slug: "b", visibility: :unlisted}, user) | ||||||
|       context_c = context_fixture(%{title: "c", visibility: :private}, user) |       context_c = context_fixture(%{slug: "c", visibility: :private}, user) | ||||||
|       assert Contexts.list_contexts(user) == [context_a, context_b, context_c] |       assert Contexts.list_contexts(user) == [context_a, context_b, context_c] | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -50,18 +50,43 @@ defmodule Memex.ContextsTest do | |||||||
|       end |       end | ||||||
|     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 |     test "create_context/1 with valid data creates a context", %{user: user} do | ||||||
|       valid_attrs = %{ |       valid_attrs = %{ | ||||||
|         "content" => "some content", |         "content" => "some content", | ||||||
|         "tags_string" => "tag1,tag2", |         "tags_string" => "tag1,tag2", | ||||||
|         "title" => "some title", |         "slug" => "some-slug", | ||||||
|         "visibility" => :public |         "visibility" => :public | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       assert {:ok, %Context{} = context} = Contexts.create_context(valid_attrs, user) |       assert {:ok, %Context{} = context} = Contexts.create_context(valid_attrs, user) | ||||||
|       assert context.content == "some content" |       assert context.content == "some content" | ||||||
|       assert context.tags == ["tag1", "tag2"] |       assert context.tags == ["tag1", "tag2"] | ||||||
|       assert context.title == "some title" |       assert context.slug == "some-slug" | ||||||
|       assert context.visibility == :public |       assert context.visibility == :public | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -75,14 +100,14 @@ defmodule Memex.ContextsTest do | |||||||
|       update_attrs = %{ |       update_attrs = %{ | ||||||
|         "content" => "some updated content", |         "content" => "some updated content", | ||||||
|         "tags_string" => "tag1,tag2", |         "tags_string" => "tag1,tag2", | ||||||
|         "title" => "some updated title", |         "slug" => "some-updated-slug", | ||||||
|         "visibility" => :private |         "visibility" => :private | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       assert {:ok, %Context{} = context} = Contexts.update_context(context, update_attrs, user) |       assert {:ok, %Context{} = context} = Contexts.update_context(context, update_attrs, user) | ||||||
|       assert context.content == "some updated content" |       assert context.content == "some updated content" | ||||||
|       assert context.tags == ["tag1", "tag2"] |       assert context.tags == ["tag1", "tag2"] | ||||||
|       assert context.title == "some updated title" |       assert context.slug == "some-updated-slug" | ||||||
|       assert context.visibility == :private |       assert context.visibility == :private | ||||||
|     end |     end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ defmodule Memex.NotesTest do | |||||||
|   import Memex.NotesFixtures |   import Memex.NotesFixtures | ||||||
|   alias Memex.{Notes, Notes.Note} |   alias Memex.{Notes, Notes.Note} | ||||||
|   @moduletag :notes_test |   @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 |   describe "notes" do | ||||||
|     setup do |     setup do | ||||||
| @@ -11,9 +11,9 @@ defmodule Memex.NotesTest do | |||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "list_notes/1 returns all notes for a user", %{user: user} do |     test "list_notes/1 returns all notes for a user", %{user: user} do | ||||||
|       note_a = note_fixture(%{title: "a", visibility: :public}, user) |       note_a = note_fixture(%{slug: "a", visibility: :public}, user) | ||||||
|       note_b = note_fixture(%{title: "b", visibility: :unlisted}, user) |       note_b = note_fixture(%{slug: "b", visibility: :unlisted}, user) | ||||||
|       note_c = note_fixture(%{title: "c", visibility: :private}, user) |       note_c = note_fixture(%{slug: "c", visibility: :private}, user) | ||||||
|       assert Notes.list_notes(user) == [note_a, note_b, note_c] |       assert Notes.list_notes(user) == [note_a, note_b, note_c] | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -50,18 +50,43 @@ defmodule Memex.NotesTest do | |||||||
|       end |       end | ||||||
|     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 |     test "create_note/1 with valid data creates a note", %{user: user} do | ||||||
|       valid_attrs = %{ |       valid_attrs = %{ | ||||||
|         "content" => "some content", |         "content" => "some content", | ||||||
|         "tags_string" => "tag1,tag2", |         "tags_string" => "tag1,tag2", | ||||||
|         "title" => "some title", |         "slug" => "some-slug", | ||||||
|         "visibility" => :public |         "visibility" => :public | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       assert {:ok, %Note{} = note} = Notes.create_note(valid_attrs, user) |       assert {:ok, %Note{} = note} = Notes.create_note(valid_attrs, user) | ||||||
|       assert note.content == "some content" |       assert note.content == "some content" | ||||||
|       assert note.tags == ["tag1", "tag2"] |       assert note.tags == ["tag1", "tag2"] | ||||||
|       assert note.title == "some title" |       assert note.slug == "some-slug" | ||||||
|       assert note.visibility == :public |       assert note.visibility == :public | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -75,14 +100,14 @@ defmodule Memex.NotesTest do | |||||||
|       update_attrs = %{ |       update_attrs = %{ | ||||||
|         "content" => "some updated content", |         "content" => "some updated content", | ||||||
|         "tags_string" => "tag1,tag2", |         "tags_string" => "tag1,tag2", | ||||||
|         "title" => "some updated title", |         "slug" => "some-updated-slug", | ||||||
|         "visibility" => :private |         "visibility" => :private | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       assert {:ok, %Note{} = note} = Notes.update_note(note, update_attrs, user) |       assert {:ok, %Note{} = note} = Notes.update_note(note, update_attrs, user) | ||||||
|       assert note.content == "some updated content" |       assert note.content == "some updated content" | ||||||
|       assert note.tags == ["tag1", "tag2"] |       assert note.tags == ["tag1", "tag2"] | ||||||
|       assert note.title == "some updated title" |       assert note.slug == "some-updated-slug" | ||||||
|       assert note.visibility == :private |       assert note.visibility == :private | ||||||
|     end |     end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ defmodule Memex.PipelinesTest do | |||||||
|   import Memex.PipelinesFixtures |   import Memex.PipelinesFixtures | ||||||
|   alias Memex.{Pipelines, Pipelines.Pipeline} |   alias Memex.{Pipelines, Pipelines.Pipeline} | ||||||
|   @moduletag :pipelines_test |   @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 |   describe "pipelines" do | ||||||
|     setup do |     setup do | ||||||
| @@ -11,9 +11,9 @@ defmodule Memex.PipelinesTest do | |||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "list_pipelines/1 returns all pipelines for a user", %{user: user} do |     test "list_pipelines/1 returns all pipelines for a user", %{user: user} do | ||||||
|       pipeline_a = pipeline_fixture(%{title: "a", visibility: :public}, user) |       pipeline_a = pipeline_fixture(%{slug: "a", visibility: :public}, user) | ||||||
|       pipeline_b = pipeline_fixture(%{title: "b", visibility: :unlisted}, user) |       pipeline_b = pipeline_fixture(%{slug: "b", visibility: :unlisted}, user) | ||||||
|       pipeline_c = pipeline_fixture(%{title: "c", visibility: :private}, user) |       pipeline_c = pipeline_fixture(%{slug: "c", visibility: :private}, user) | ||||||
|       assert Pipelines.list_pipelines(user) == [pipeline_a, pipeline_b, pipeline_c] |       assert Pipelines.list_pipelines(user) == [pipeline_a, pipeline_b, pipeline_c] | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -52,18 +52,43 @@ defmodule Memex.PipelinesTest do | |||||||
|       end |       end | ||||||
|     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 |     test "create_pipeline/1 with valid data creates a pipeline", %{user: user} do | ||||||
|       valid_attrs = %{ |       valid_attrs = %{ | ||||||
|         "description" => "some description", |         "description" => "some description", | ||||||
|         "tags_string" => "tag1,tag2", |         "tags_string" => "tag1,tag2", | ||||||
|         "title" => "some title", |         "slug" => "some-slug", | ||||||
|         "visibility" => :public |         "visibility" => :public | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       assert {:ok, %Pipeline{} = pipeline} = Pipelines.create_pipeline(valid_attrs, user) |       assert {:ok, %Pipeline{} = pipeline} = Pipelines.create_pipeline(valid_attrs, user) | ||||||
|       assert pipeline.description == "some description" |       assert pipeline.description == "some description" | ||||||
|       assert pipeline.tags == ["tag1", "tag2"] |       assert pipeline.tags == ["tag1", "tag2"] | ||||||
|       assert pipeline.title == "some title" |       assert pipeline.slug == "some-slug" | ||||||
|       assert pipeline.visibility == :public |       assert pipeline.visibility == :public | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -77,7 +102,7 @@ defmodule Memex.PipelinesTest do | |||||||
|       update_attrs = %{ |       update_attrs = %{ | ||||||
|         "description" => "some updated description", |         "description" => "some updated description", | ||||||
|         "tags_string" => "tag1,tag2", |         "tags_string" => "tag1,tag2", | ||||||
|         "title" => "some updated title", |         "slug" => "some-updated-slug", | ||||||
|         "visibility" => :private |         "visibility" => :private | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -86,7 +111,7 @@ defmodule Memex.PipelinesTest do | |||||||
|  |  | ||||||
|       assert pipeline.description == "some updated description" |       assert pipeline.description == "some updated description" | ||||||
|       assert pipeline.tags == ["tag1", "tag2"] |       assert pipeline.tags == ["tag1", "tag2"] | ||||||
|       assert pipeline.title == "some updated title" |       assert pipeline.slug == "some-updated-slug" | ||||||
|       assert pipeline.visibility == :private |       assert pipeline.visibility == :private | ||||||
|     end |     end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,19 +7,19 @@ defmodule MemexWeb.ContextLiveTest do | |||||||
|   @create_attrs %{ |   @create_attrs %{ | ||||||
|     "content" => "some content", |     "content" => "some content", | ||||||
|     "tags_string" => "tag1", |     "tags_string" => "tag1", | ||||||
|     "title" => "some title", |     "slug" => "some-slug", | ||||||
|     "visibility" => :public |     "visibility" => :public | ||||||
|   } |   } | ||||||
|   @update_attrs %{ |   @update_attrs %{ | ||||||
|     "content" => "some updated content", |     "content" => "some updated content", | ||||||
|     "tags_string" => "tag1,tag2", |     "tags_string" => "tag1,tag2", | ||||||
|     "title" => "some updated title", |     "slug" => "some-updated-slug", | ||||||
|     "visibility" => :private |     "visibility" => :private | ||||||
|   } |   } | ||||||
|   @invalid_attrs %{ |   @invalid_attrs %{ | ||||||
|     "content" => nil, |     "content" => nil, | ||||||
|     "tags_string" => "", |     "tags_string" => "", | ||||||
|     "title" => nil, |     "slug" => nil, | ||||||
|     "visibility" => nil |     "visibility" => nil | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -55,7 +55,7 @@ defmodule MemexWeb.ContextLiveTest do | |||||||
|         |> render_submit() |         |> render_submit() | ||||||
|         |> follow_redirect(conn, Routes.context_index_path(conn, :index)) |         |> 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" |       assert html =~ "some content" | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -65,7 +65,7 @@ defmodule MemexWeb.ContextLiveTest do | |||||||
|       assert index_live |> element("[data-qa=\"context-edit-#{context.id}\"]") |> render_click() =~ |       assert index_live |> element("[data-qa=\"context-edit-#{context.id}\"]") |> render_click() =~ | ||||||
|                "edit" |                "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 |       assert index_live | ||||||
|              |> form("#context-form", context: @invalid_attrs) |              |> form("#context-form", context: @invalid_attrs) | ||||||
| @@ -77,7 +77,7 @@ defmodule MemexWeb.ContextLiveTest do | |||||||
|         |> render_submit() |         |> render_submit() | ||||||
|         |> follow_redirect(conn, Routes.context_index_path(conn, :index)) |         |> 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" |       assert html =~ "some updated content" | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -93,18 +93,18 @@ defmodule MemexWeb.ContextLiveTest do | |||||||
|     setup [:register_and_log_in_user, :create_context] |     setup [:register_and_log_in_user, :create_context] | ||||||
|  |  | ||||||
|     test "displays context", %{conn: conn, context: context} do |     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" | ||||||
|       assert html =~ context.content |       assert html =~ context.content | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "updates context within modal", %{conn: conn, context: context} do |     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 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 |       assert show_live | ||||||
|              |> form("#context-form", context: @invalid_attrs) |              |> form("#context-form", context: @invalid_attrs) | ||||||
| @@ -112,16 +112,16 @@ defmodule MemexWeb.ContextLiveTest do | |||||||
|  |  | ||||||
|       {:ok, _, html} = |       {:ok, _, html} = | ||||||
|         show_live |         show_live | ||||||
|         |> form("#context-form", context: @update_attrs) |         |> form("#context-form", context: Map.put(@update_attrs, "slug", context.slug)) | ||||||
|         |> render_submit() |         |> 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" |       assert html =~ "some updated content" | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "deletes context", %{conn: conn, context: context} do |     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} = |       {:ok, index_live, _html} = | ||||||
|         show_live |         show_live | ||||||
|   | |||||||
| @@ -7,19 +7,19 @@ defmodule MemexWeb.NoteLiveTest do | |||||||
|   @create_attrs %{ |   @create_attrs %{ | ||||||
|     "content" => "some content", |     "content" => "some content", | ||||||
|     "tags_string" => "tag1", |     "tags_string" => "tag1", | ||||||
|     "title" => "some title", |     "slug" => "some-slug", | ||||||
|     "visibility" => :public |     "visibility" => :public | ||||||
|   } |   } | ||||||
|   @update_attrs %{ |   @update_attrs %{ | ||||||
|     "content" => "some updated content", |     "content" => "some updated content", | ||||||
|     "tags_string" => "tag1,tag2", |     "tags_string" => "tag1,tag2", | ||||||
|     "title" => "some updated title", |     "slug" => "some-updated-slug", | ||||||
|     "visibility" => :private |     "visibility" => :private | ||||||
|   } |   } | ||||||
|   @invalid_attrs %{ |   @invalid_attrs %{ | ||||||
|     "content" => nil, |     "content" => nil, | ||||||
|     "tags_string" => "", |     "tags_string" => "", | ||||||
|     "title" => nil, |     "slug" => nil, | ||||||
|     "visibility" => nil |     "visibility" => nil | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -55,7 +55,7 @@ defmodule MemexWeb.NoteLiveTest do | |||||||
|         |> render_submit() |         |> render_submit() | ||||||
|         |> follow_redirect(conn, Routes.note_index_path(conn, :index)) |         |> 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" |       assert html =~ "some content" | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -65,7 +65,7 @@ defmodule MemexWeb.NoteLiveTest do | |||||||
|       assert index_live |> element("[data-qa=\"note-edit-#{note.id}\"]") |> render_click() =~ |       assert index_live |> element("[data-qa=\"note-edit-#{note.id}\"]") |> render_click() =~ | ||||||
|                "edit" |                "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 |       assert index_live | ||||||
|              |> form("#note-form", note: @invalid_attrs) |              |> form("#note-form", note: @invalid_attrs) | ||||||
| @@ -77,7 +77,7 @@ defmodule MemexWeb.NoteLiveTest do | |||||||
|         |> render_submit() |         |> render_submit() | ||||||
|         |> follow_redirect(conn, Routes.note_index_path(conn, :index)) |         |> 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" |       assert html =~ "some updated content" | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -93,18 +93,18 @@ defmodule MemexWeb.NoteLiveTest do | |||||||
|     setup [:register_and_log_in_user, :create_note] |     setup [:register_and_log_in_user, :create_note] | ||||||
|  |  | ||||||
|     test "displays note", %{conn: conn, note: note} do |     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" | ||||||
|       assert html =~ note.content |       assert html =~ note.content | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "updates note within modal", %{conn: conn, note: note} do |     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 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 |       assert show_live | ||||||
|              |> form("#note-form", note: @invalid_attrs) |              |> form("#note-form", note: @invalid_attrs) | ||||||
| @@ -112,16 +112,16 @@ defmodule MemexWeb.NoteLiveTest do | |||||||
|  |  | ||||||
|       {:ok, _, html} = |       {:ok, _, html} = | ||||||
|         show_live |         show_live | ||||||
|         |> form("#note-form", note: @update_attrs) |         |> form("#note-form", note: Map.put(@update_attrs, "slug", note.slug)) | ||||||
|         |> render_submit() |         |> 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" |       assert html =~ "some updated content" | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "deletes note", %{conn: conn, note: note} do |     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} = |       {:ok, index_live, _html} = | ||||||
|         show_live |         show_live | ||||||
|   | |||||||
| @@ -7,19 +7,19 @@ defmodule MemexWeb.PipelineLiveTest do | |||||||
|   @create_attrs %{ |   @create_attrs %{ | ||||||
|     "description" => "some description", |     "description" => "some description", | ||||||
|     "tags_string" => "tag1", |     "tags_string" => "tag1", | ||||||
|     "title" => "some title", |     "slug" => "some-slug", | ||||||
|     "visibility" => :public |     "visibility" => :public | ||||||
|   } |   } | ||||||
|   @update_attrs %{ |   @update_attrs %{ | ||||||
|     "description" => "some updated description", |     "description" => "some updated description", | ||||||
|     "tags_string" => "tag1,tag2", |     "tags_string" => "tag1,tag2", | ||||||
|     "title" => "some updated title", |     "slug" => "some-updated-slug", | ||||||
|     "visibility" => :private |     "visibility" => :private | ||||||
|   } |   } | ||||||
|   @invalid_attrs %{ |   @invalid_attrs %{ | ||||||
|     "description" => nil, |     "description" => nil, | ||||||
|     "tags_string" => "", |     "tags_string" => "", | ||||||
|     "title" => nil, |     "slug" => nil, | ||||||
|     "visibility" => nil |     "visibility" => nil | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -55,7 +55,7 @@ defmodule MemexWeb.PipelineLiveTest do | |||||||
|         |> render_submit() |         |> render_submit() | ||||||
|         |> follow_redirect(conn, Routes.pipeline_index_path(conn, :index)) |         |> 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" |       assert html =~ "some description" | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -65,7 +65,7 @@ defmodule MemexWeb.PipelineLiveTest do | |||||||
|       assert index_live |> element("[data-qa=\"pipeline-edit-#{pipeline.id}\"]") |> render_click() =~ |       assert index_live |> element("[data-qa=\"pipeline-edit-#{pipeline.id}\"]") |> render_click() =~ | ||||||
|                "edit" |                "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 |       assert index_live | ||||||
|              |> form("#pipeline-form", pipeline: @invalid_attrs) |              |> form("#pipeline-form", pipeline: @invalid_attrs) | ||||||
| @@ -77,7 +77,7 @@ defmodule MemexWeb.PipelineLiveTest do | |||||||
|         |> render_submit() |         |> render_submit() | ||||||
|         |> follow_redirect(conn, Routes.pipeline_index_path(conn, :index)) |         |> 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" |       assert html =~ "some updated description" | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -96,18 +96,18 @@ defmodule MemexWeb.PipelineLiveTest do | |||||||
|     setup [:register_and_log_in_user, :create_pipeline] |     setup [:register_and_log_in_user, :create_pipeline] | ||||||
|  |  | ||||||
|     test "displays pipeline", %{conn: conn, pipeline: pipeline} do |     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" | ||||||
|       assert html =~ pipeline.description |       assert html =~ pipeline.description | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "updates pipeline within modal", %{conn: conn, pipeline: pipeline} do |     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 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 |       assert show_live | ||||||
|              |> form("#pipeline-form", pipeline: @invalid_attrs) |              |> form("#pipeline-form", pipeline: @invalid_attrs) | ||||||
| @@ -115,16 +115,16 @@ defmodule MemexWeb.PipelineLiveTest do | |||||||
|  |  | ||||||
|       {:ok, _, html} = |       {:ok, _, html} = | ||||||
|         show_live |         show_live | ||||||
|         |> form("#pipeline-form", pipeline: @update_attrs) |         |> form("#pipeline-form", pipeline: Map.put(@update_attrs, "slug", pipeline.slug)) | ||||||
|         |> render_submit() |         |> 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" |       assert html =~ "some updated description" | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "deletes pipeline", %{conn: conn, pipeline: pipeline} do |     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} = |       {:ok, index_live, _html} = | ||||||
|         show_live |         show_live | ||||||
|   | |||||||
| @@ -13,11 +13,11 @@ defmodule MemexWeb.ErrorViewTest do | |||||||
|  |  | ||||||
|   test "renders 404.html" do |   test "renders 404.html" do | ||||||
|     assert render_to_string(MemexWeb.ErrorView, "404.html", []) =~ |     assert render_to_string(MemexWeb.ErrorView, "404.html", []) =~ | ||||||
|              dgettext("errors", "Not found") |              dgettext("errors", "not found") | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   test "renders 500.html" do |   test "renders 500.html" do | ||||||
|     assert render_to_string(MemexWeb.ErrorView, "500.html", []) =~ |     assert render_to_string(MemexWeb.ErrorView, "500.html", []) =~ | ||||||
|              dgettext("errors", "Internal Server Error") |              dgettext("errors", "internal server error") | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ defmodule Memex.Fixtures do | |||||||
|   @moduledoc """ |   @moduledoc """ | ||||||
|   This module defines test helpers for creating entities |   This module defines test helpers for creating entities | ||||||
|   """ |   """ | ||||||
|  |  | ||||||
|   alias Memex.{Accounts, Accounts.User, Email, Repo} |   alias Memex.{Accounts, Accounts.User, Email, Repo} | ||||||
|  |  | ||||||
|   def unique_user_email, do: "user#{System.unique_integer()}@example.com" |   def unique_user_email, do: "user#{System.unique_integer()}@example.com" | ||||||
| @@ -57,5 +56,14 @@ defmodule Memex.Fixtures do | |||||||
|     }) |     }) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def random_slug(length \\ 20) do | ||||||
|  |     symbols = '0123456789abcdef-' | ||||||
|  |     symbol_count = Enum.count(symbols) | ||||||
|  |  | ||||||
|  |     for _ <- Range.new(1, length), | ||||||
|  |         into: "", | ||||||
|  |         do: <<Enum.at(symbols, :rand.uniform(symbol_count - 1))>> | ||||||
|  |   end | ||||||
|  |  | ||||||
|   defp unwrap_ok_tuple({:ok, value}), do: value |   defp unwrap_ok_tuple({:ok, value}), do: value | ||||||
| end | end | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ defmodule Memex.ContextsFixtures do | |||||||
|   This module defines test helpers for creating |   This module defines test helpers for creating | ||||||
|   entities via the `Memex.Contexts` context. |   entities via the `Memex.Contexts` context. | ||||||
|   """ |   """ | ||||||
|  |   import Memex.Fixtures | ||||||
|   alias Memex.{Accounts.User, Contexts, Contexts.Context} |   alias Memex.{Accounts.User, Contexts, Contexts.Context} | ||||||
|  |  | ||||||
|   @doc """ |   @doc """ | ||||||
| @@ -16,7 +17,7 @@ defmodule Memex.ContextsFixtures do | |||||||
|       |> Enum.into(%{ |       |> Enum.into(%{ | ||||||
|         content: "some content", |         content: "some content", | ||||||
|         tag: [], |         tag: [], | ||||||
|         title: "some title", |         slug: random_slug(), | ||||||
|         visibility: :private |         visibility: :private | ||||||
|       }) |       }) | ||||||
|       |> Contexts.create_context(user) |       |> Contexts.create_context(user) | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ defmodule Memex.NotesFixtures do | |||||||
|   This module defines test helpers for creating |   This module defines test helpers for creating | ||||||
|   entities via the `Memex.Notes` context. |   entities via the `Memex.Notes` context. | ||||||
|   """ |   """ | ||||||
|  |   import Memex.Fixtures | ||||||
|   alias Memex.{Accounts.User, Notes, Notes.Note} |   alias Memex.{Accounts.User, Notes, Notes.Note} | ||||||
|  |  | ||||||
|   @doc """ |   @doc """ | ||||||
| @@ -16,7 +17,7 @@ defmodule Memex.NotesFixtures do | |||||||
|       |> Enum.into(%{ |       |> Enum.into(%{ | ||||||
|         content: "some content", |         content: "some content", | ||||||
|         tag: [], |         tag: [], | ||||||
|         title: "some title", |         slug: random_slug(), | ||||||
|         visibility: :private |         visibility: :private | ||||||
|       }) |       }) | ||||||
|       |> Notes.create_note(user) |       |> Notes.create_note(user) | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ defmodule Memex.PipelinesFixtures do | |||||||
|   This module defines test helpers for creating |   This module defines test helpers for creating | ||||||
|   entities via the `Memex.Pipelines` context. |   entities via the `Memex.Pipelines` context. | ||||||
|   """ |   """ | ||||||
|  |   import Memex.Fixtures | ||||||
|   alias Memex.{Accounts.User, Pipelines, Pipelines.Pipeline} |   alias Memex.{Accounts.User, Pipelines, Pipelines.Pipeline} | ||||||
|  |  | ||||||
|   @doc """ |   @doc """ | ||||||
| @@ -16,7 +17,7 @@ defmodule Memex.PipelinesFixtures do | |||||||
|       |> Enum.into(%{ |       |> Enum.into(%{ | ||||||
|         description: "some description", |         description: "some description", | ||||||
|         tag: [], |         tag: [], | ||||||
|         title: "some title", |         slug: random_slug(), | ||||||
|         visibility: :private |         visibility: :private | ||||||
|       }) |       }) | ||||||
|       |> Pipelines.create_pipeline(user) |       |> Pipelines.create_pipeline(user) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user