Compare commits
10 Commits
8ae56a8bc1
...
fa866cc10e
Author | SHA1 | Date |
---|---|---|
Inhji | fa866cc10e | |
Inhji | 127757b12a | |
Inhji | 370bdfe99d | |
Inhji | 13d1a67a0e | |
Inhji | 4d20f5c4d0 | |
Inhji | 58d11699f3 | |
Inhji | 26e6bf2c42 | |
Inhji | 21ee3298b6 | |
Inhji | ae59609b28 | |
Inhji | e9038c1636 |
|
@ -59,6 +59,11 @@ config :logger, :console,
|
|||
# Use Jason for JSON parsing in Phoenix
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
config :chiya, Oban,
|
||||
repo: Chiya.Repo,
|
||||
plugins: [Oban.Plugins.Pruner],
|
||||
queues: [default: 10]
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{config_env()}.exs"
|
||||
|
|
|
@ -34,3 +34,6 @@ config :logger, level: :warning
|
|||
|
||||
# Initialize plugs at runtime for faster test compilation
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
# Prevent oban from running jobs and plugins during test
|
||||
config :chiya, Oban, testing: :inline
|
||||
|
|
|
@ -17,7 +17,9 @@ defmodule Chiya.Application do
|
|||
# Start Finch
|
||||
{Finch, name: Chiya.Finch},
|
||||
# Start the Endpoint (http/https)
|
||||
ChiyaWeb.Endpoint
|
||||
ChiyaWeb.Endpoint,
|
||||
# Start Scheduler
|
||||
{Oban, Application.fetch_env!(:chiya, Oban)}
|
||||
# Start a worker by calling: Chiya.Worker.start_link(arg)
|
||||
# {Chiya.Worker, arg}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
defmodule Chiya.Channels do
|
||||
@moduledoc """
|
||||
The Channels context.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias Chiya.Repo
|
||||
|
||||
alias Chiya.Channels.Channel
|
||||
|
||||
@doc """
|
||||
Returns the list of channels.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_channels()
|
||||
[%Channel{}, ...]
|
||||
|
||||
"""
|
||||
def list_channels do
|
||||
Repo.all(Channel)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single channel.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Channel does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_channel!(123)
|
||||
%Channel{}
|
||||
|
||||
iex> get_channel!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_channel!(id), do: Repo.get!(Channel, id)
|
||||
|
||||
@doc """
|
||||
Creates a channel.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_channel(%{field: value})
|
||||
{:ok, %Channel{}}
|
||||
|
||||
iex> create_channel(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_channel(attrs \\ %{}) do
|
||||
%Channel{}
|
||||
|> Channel.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a channel.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_channel(channel, %{field: new_value})
|
||||
{:ok, %Channel{}}
|
||||
|
||||
iex> update_channel(channel, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_channel(%Channel{} = channel, attrs) do
|
||||
channel
|
||||
|> Channel.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a channel.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_channel(channel)
|
||||
{:ok, %Channel{}}
|
||||
|
||||
iex> delete_channel(channel)
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def delete_channel(%Channel{} = channel) do
|
||||
Repo.delete(channel)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking channel changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_channel(channel)
|
||||
%Ecto.Changeset{data: %Channel{}}
|
||||
|
||||
"""
|
||||
def change_channel(%Channel{} = channel, attrs \\ %{}) do
|
||||
Channel.changeset(channel, attrs)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
defmodule Chiya.Channels.Channel do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "channels" do
|
||||
field :content, :string
|
||||
field :name, :string
|
||||
field :slug, :string
|
||||
field :visibility, Ecto.Enum, values: [:public, :private, :unlisted]
|
||||
|
||||
many_to_many :notes, Chiya.Notes.Note, join_through: "channels_notes"
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(channel, attrs) do
|
||||
channel
|
||||
|> cast(attrs, [:name, :content, :visibility, :slug])
|
||||
|> validate_required([:name, :content, :visibility, :slug])
|
||||
|> unique_constraint(:slug)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,132 @@
|
|||
defmodule Chiya.Notes do
|
||||
@moduledoc """
|
||||
The Notes context.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias Chiya.Repo
|
||||
|
||||
alias Chiya.Notes.Note
|
||||
|
||||
@preloads [:channels]
|
||||
|
||||
@doc """
|
||||
Returns the list of notes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_notes()
|
||||
[%Note{}, ...]
|
||||
|
||||
"""
|
||||
def list_notes do
|
||||
Repo.all(Note) |> Repo.preload(@preloads)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Preloads a note
|
||||
|
||||
## Examples
|
||||
|
||||
iex> preload_note(note)
|
||||
%Note{}
|
||||
"""
|
||||
def preload_note(note), do: Repo.preload(note, @preloads)
|
||||
|
||||
@doc """
|
||||
Gets a single note.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Note does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_note!(123)
|
||||
%Note{}
|
||||
|
||||
iex> get_note!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_note!(id), do: Repo.get!(Note, id)
|
||||
|
||||
@doc """
|
||||
Gets a single note and preloads it.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Note does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_note_preloaded!(123)
|
||||
%Note{}
|
||||
|
||||
iex> get_note_preloaded!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_note_preloaded!(id), do: Repo.get!(Note, id) |> preload_note()
|
||||
|
||||
@doc """
|
||||
Creates a note.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_note(%{field: value})
|
||||
{:ok, %Note{}}
|
||||
|
||||
iex> create_note(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_note(attrs \\ %{}) do
|
||||
%Note{}
|
||||
|> Note.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a note.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_note(note, %{field: new_value})
|
||||
{:ok, %Note{}}
|
||||
|
||||
iex> update_note(note, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_note(%Note{} = note, attrs) do
|
||||
note
|
||||
|> Note.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a note.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_note(note)
|
||||
{:ok, %Note{}}
|
||||
|
||||
iex> delete_note(note)
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def delete_note(%Note{} = note) do
|
||||
Repo.delete(note)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking note changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_note(note)
|
||||
%Ecto.Changeset{data: %Note{}}
|
||||
|
||||
"""
|
||||
def change_note(%Note{} = note, attrs \\ %{}) do
|
||||
Note.changeset(note, attrs)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
defmodule Chiya.Notes.Note do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "notes" do
|
||||
field :content, :string
|
||||
field :kind, :string
|
||||
field :name, :string
|
||||
field :published_at, :naive_datetime
|
||||
field :slug, :string
|
||||
field :url, :string
|
||||
|
||||
many_to_many :channels, Chiya.Channels.Channel,
|
||||
# join_through: Chiya.Channels.ChannelNote,
|
||||
join_through: "channels_notes",
|
||||
join_keys: [note: :id, channel: :id],
|
||||
on_replace: :delete
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(note, attrs) do
|
||||
note
|
||||
|> Chiya.Notes.preload_note()
|
||||
|> cast(attrs, [:name, :content, :slug, :published_at, :kind, :url])
|
||||
|> put_assoc(:channels, attrs["channels"] || [])
|
||||
|> validate_required([:name, :content, :slug, :published_at, :kind, :url])
|
||||
|> unique_constraint(:slug)
|
||||
end
|
||||
end
|
|
@ -14,24 +14,18 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<a
|
||||
href="https://twitter.com/elixirphoenix"
|
||||
<.link
|
||||
href={~p"/channels"}
|
||||
class="text-[0.8125rem] font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
|
||||
>
|
||||
@elixirphoenix
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/phoenixframework/phoenix"
|
||||
Channels
|
||||
</.link>
|
||||
<.link
|
||||
href={~p"/notes"}
|
||||
class="text-[0.8125rem] font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
<a
|
||||
href="https://hexdocs.pm/phoenix/overview.html"
|
||||
class="rounded-lg bg-zinc-100 px-2 py-1 text-[0.8125rem] font-semibold leading-6 text-zinc-900 hover:bg-zinc-200/80 active:text-zinc-900/70"
|
||||
>
|
||||
Get Started <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
Notes
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</script>
|
||||
</head>
|
||||
<body class="bg-white antialiased">
|
||||
<ul class="relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
|
||||
<ul class="relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end bg-black">
|
||||
<%= if @current_user do %>
|
||||
<li class="text-[0.8125rem] leading-6 text-zinc-900">
|
||||
<%= @current_user.email %>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<li>
|
||||
<.link
|
||||
href={~p"/users/settings"}
|
||||
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
|
||||
class="text-[0.8125rem] leading-6 text-zinc-100 font-semibold hover:text-zinc-300"
|
||||
>
|
||||
Settings
|
||||
</.link>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<.link
|
||||
href={~p"/users/log_out"}
|
||||
method="delete"
|
||||
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
|
||||
class="text-[0.8125rem] leading-6 text-zinc-100 font-semibold hover:text-zinc-300"
|
||||
>
|
||||
Log out
|
||||
</.link>
|
||||
|
@ -38,7 +38,7 @@
|
|||
<li>
|
||||
<.link
|
||||
href={~p"/users/register"}
|
||||
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
|
||||
class="text-[0.8125rem] leading-6 text-zinc-100 font-semibold hover:text-zinc-300"
|
||||
>
|
||||
Register
|
||||
</.link>
|
||||
|
@ -46,7 +46,7 @@
|
|||
<li>
|
||||
<.link
|
||||
href={~p"/users/log_in"}
|
||||
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
|
||||
class="text-[0.8125rem] leading-6 text-zinc-100 font-semibold hover:text-zinc-300"
|
||||
>
|
||||
Log in
|
||||
</.link>
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
defmodule ChiyaWeb.ChannelController do
|
||||
use ChiyaWeb, :controller
|
||||
|
||||
alias Chiya.Channels
|
||||
alias Chiya.Channels.Channel
|
||||
|
||||
def index(conn, _params) do
|
||||
channels = Channels.list_channels()
|
||||
render(conn, :index, channels: channels)
|
||||
end
|
||||
|
||||
def new(conn, _params) do
|
||||
changeset = Channels.change_channel(%Channel{})
|
||||
render(conn, :new, changeset: changeset)
|
||||
end
|
||||
|
||||
def create(conn, %{"channel" => channel_params}) do
|
||||
case Channels.create_channel(channel_params) do
|
||||
{:ok, channel} ->
|
||||
conn
|
||||
|> put_flash(:info, "Channel created successfully.")
|
||||
|> redirect(to: ~p"/channels/#{channel}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :new, changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
def show(conn, %{"id" => id}) do
|
||||
channel = Channels.get_channel!(id)
|
||||
render(conn, :show, channel: channel)
|
||||
end
|
||||
|
||||
def edit(conn, %{"id" => id}) do
|
||||
channel = Channels.get_channel!(id)
|
||||
changeset = Channels.change_channel(channel)
|
||||
render(conn, :edit, channel: channel, changeset: changeset)
|
||||
end
|
||||
|
||||
def update(conn, %{"id" => id, "channel" => channel_params}) do
|
||||
channel = Channels.get_channel!(id)
|
||||
|
||||
case Channels.update_channel(channel, channel_params) do
|
||||
{:ok, channel} ->
|
||||
conn
|
||||
|> put_flash(:info, "Channel updated successfully.")
|
||||
|> redirect(to: ~p"/channels/#{channel}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :edit, channel: channel, changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
def delete(conn, %{"id" => id}) do
|
||||
channel = Channels.get_channel!(id)
|
||||
{:ok, _channel} = Channels.delete_channel(channel)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Channel deleted successfully.")
|
||||
|> redirect(to: ~p"/channels")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
defmodule ChiyaWeb.ChannelHTML do
|
||||
use ChiyaWeb, :html
|
||||
|
||||
embed_templates "channel_html/*"
|
||||
|
||||
@doc """
|
||||
Renders a channel form.
|
||||
"""
|
||||
attr :changeset, Ecto.Changeset, required: true
|
||||
attr :action, :string, required: true
|
||||
|
||||
def channel_form(assigns)
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
<.simple_form :let={f} for={@changeset} action={@action}>
|
||||
<.error :if={@changeset.action}>
|
||||
Oops, something went wrong! Please check the errors below.
|
||||
</.error>
|
||||
<.input field={f[:name]} type="text" label="Name" />
|
||||
<.input field={f[:content]} type="textarea" label="Content" />
|
||||
<.input
|
||||
field={f[:visibility]}
|
||||
type="select"
|
||||
label="Visibility"
|
||||
prompt="Choose a value"
|
||||
options={Ecto.Enum.values(Chiya.Channels.Channel, :visibility)}
|
||||
/>
|
||||
<.input field={f[:slug]} type="text" label="Slug" />
|
||||
<:actions>
|
||||
<.button>Save Channel</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
|
@ -0,0 +1,8 @@
|
|||
<.header>
|
||||
Edit Channel <%= @channel.id %>
|
||||
<:subtitle>Use this form to manage channel records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.channel_form changeset={@changeset} action={~p"/channels/#{@channel}"} />
|
||||
|
||||
<.back navigate={~p"/channels"}>Back to channels</.back>
|
|
@ -0,0 +1,25 @@
|
|||
<.header>
|
||||
Listing Channels
|
||||
<:actions>
|
||||
<.link href={~p"/channels/new"}>
|
||||
<.button>New Channel</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.table id="channels" rows={@channels} row_click={&JS.navigate(~p"/channels/#{&1}")}>
|
||||
<:col :let={channel} label="Name"><%= channel.name %></:col>
|
||||
<:col :let={channel} label="Visibility"><%= channel.visibility %></:col>
|
||||
<:col :let={channel} label="Slug"><%= channel.slug %></:col>
|
||||
<:action :let={channel}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/channels/#{channel}"}>Show</.link>
|
||||
</div>
|
||||
<.link navigate={~p"/channels/#{channel}/edit"}>Edit</.link>
|
||||
</:action>
|
||||
<:action :let={channel}>
|
||||
<.link href={~p"/channels/#{channel}"} method="delete" data-confirm="Are you sure?">
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
|
@ -0,0 +1,8 @@
|
|||
<.header>
|
||||
New Channel
|
||||
<:subtitle>Use this form to manage channel records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.channel_form changeset={@changeset} action={~p"/channels"} />
|
||||
|
||||
<.back navigate={~p"/channels"}>Back to channels</.back>
|
|
@ -0,0 +1,18 @@
|
|||
<.header>
|
||||
Channel <%= @channel.id %>
|
||||
<:subtitle>This is a channel record from your database.</:subtitle>
|
||||
<:actions>
|
||||
<.link href={~p"/channels/#{@channel}/edit"}>
|
||||
<.button>Edit channel</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.list>
|
||||
<:item title="Name"><%= @channel.name %></:item>
|
||||
<:item title="Content"><%= @channel.content %></:item>
|
||||
<:item title="Visibility"><%= @channel.visibility %></:item>
|
||||
<:item title="Slug"><%= @channel.slug %></:item>
|
||||
</.list>
|
||||
|
||||
<.back navigate={~p"/channels"}>Back to channels</.back>
|
|
@ -0,0 +1,78 @@
|
|||
defmodule ChiyaWeb.NoteController do
|
||||
use ChiyaWeb, :controller
|
||||
|
||||
alias Chiya.Notes
|
||||
alias Chiya.Notes.Note
|
||||
|
||||
def index(conn, _params) do
|
||||
notes = Notes.list_notes()
|
||||
render(conn, :index, notes: notes)
|
||||
end
|
||||
|
||||
def new(conn, _params) do
|
||||
changeset = Notes.change_note(%Note{})
|
||||
render(conn, :new, changeset: changeset, channels: to_channel_options())
|
||||
end
|
||||
|
||||
def create(conn, %{"note" => note_params}) do
|
||||
note_params = from_channel_ids(note_params)
|
||||
|
||||
case Notes.create_note(note_params) do
|
||||
{:ok, note} ->
|
||||
conn
|
||||
|> put_flash(:info, "Note created successfully.")
|
||||
|> redirect(to: ~p"/notes/#{note}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :new, changeset: changeset, channels: to_channel_options())
|
||||
end
|
||||
end
|
||||
|
||||
def show(conn, %{"id" => id}) do
|
||||
note = Notes.get_note!(id)
|
||||
render(conn, :show, note: note)
|
||||
end
|
||||
|
||||
def edit(conn, %{"id" => id}) do
|
||||
note = Notes.get_note_preloaded!(id)
|
||||
changeset = Notes.change_note(note)
|
||||
render(conn, :edit, note: note, changeset: changeset, channels: to_channel_options())
|
||||
end
|
||||
|
||||
def update(conn, %{"id" => id, "note" => note_params}) do
|
||||
note_params = from_channel_ids(note_params)
|
||||
note = Notes.get_note_preloaded!(id)
|
||||
|
||||
case Notes.update_note(note, note_params) do
|
||||
{:ok, note} ->
|
||||
conn
|
||||
|> put_flash(:info, "Note updated successfully.")
|
||||
|> redirect(to: ~p"/notes/#{note}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :edit, note: note, changeset: changeset, channels: to_channel_options())
|
||||
end
|
||||
end
|
||||
|
||||
def delete(conn, %{"id" => id}) do
|
||||
note = Notes.get_note!(id)
|
||||
{:ok, _note} = Notes.delete_note(note)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Note deleted successfully.")
|
||||
|> redirect(to: ~p"/notes")
|
||||
end
|
||||
|
||||
defp from_channel_ids(note_params) do
|
||||
selected_ids = Enum.map(note_params["channels"] || [], &String.to_integer/1)
|
||||
|
||||
selected_channels =
|
||||
Chiya.Channels.list_channels()
|
||||
|> Enum.filter(fn c -> Enum.member?(selected_ids, c.id) end)
|
||||
|
||||
Map.put(note_params, "channels", selected_channels)
|
||||
end
|
||||
|
||||
defp to_channel_options(items \\ nil),
|
||||
do: Enum.map(items || Chiya.Channels.list_channels(), fn c -> {c.name, c.id} end)
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
defmodule ChiyaWeb.NoteHTML do
|
||||
use ChiyaWeb, :html
|
||||
|
||||
embed_templates "note_html/*"
|
||||
|
||||
@doc """
|
||||
Renders a note form.
|
||||
"""
|
||||
attr :changeset, Ecto.Changeset, required: true
|
||||
attr :action, :string, required: true
|
||||
attr :channels, :list, required: true
|
||||
|
||||
def note_form(assigns)
|
||||
|
||||
def selected_channels(changeset), do: Enum.map(changeset.data.channels, fn c -> c.id end)
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
<.header>
|
||||
Edit Note <%= @note.id %>
|
||||
<:subtitle>Use this form to manage note records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.note_form changeset={@changeset} action={~p"/notes/#{@note}"} channels={@channels} />
|
||||
|
||||
<.back navigate={~p"/notes"}>Back to notes</.back>
|
|
@ -0,0 +1,27 @@
|
|||
<.header>
|
||||
Listing Notes
|
||||
<:actions>
|
||||
<.link href={~p"/notes/new"}>
|
||||
<.button>New Note</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.table id="notes" rows={@notes} row_click={&JS.navigate(~p"/notes/#{&1}")}>
|
||||
<:col :let={note} label="Name"><%= note.name %></:col>
|
||||
<:col :let={note} label="Slug"><%= note.slug %></:col>
|
||||
<:col :let={note} label="Published at"><%= note.published_at %></:col>
|
||||
<:col :let={note} label="Kind"><%= note.kind %></:col>
|
||||
<:col :let={note} label="Url"><%= note.url %></:col>
|
||||
<:action :let={note}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/notes/#{note}"}>Show</.link>
|
||||
</div>
|
||||
<.link navigate={~p"/notes/#{note}/edit"}>Edit</.link>
|
||||
</:action>
|
||||
<:action :let={note}>
|
||||
<.link href={~p"/notes/#{note}"} method="delete" data-confirm="Are you sure?">
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
|
@ -0,0 +1,8 @@
|
|||
<.header>
|
||||
New Note
|
||||
<:subtitle>Use this form to manage note records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.note_form changeset={@changeset} action={~p"/notes"} channels={@channels} />
|
||||
|
||||
<.back navigate={~p"/notes"}>Back to notes</.back>
|
|
@ -0,0 +1,22 @@
|
|||
<.simple_form :let={f} for={@changeset} action={@action}>
|
||||
<.error :if={@changeset.action}>
|
||||
Oops, something went wrong! Please check the errors below.
|
||||
</.error>
|
||||
<.input field={f[:name]} type="text" label="Name" />
|
||||
<.input field={f[:content]} type="textarea" label="Content" />
|
||||
<.input field={f[:slug]} type="text" label="Slug" />
|
||||
<.input field={f[:published_at]} type="datetime-local" label="Published at" />
|
||||
<.input field={f[:kind]} type="text" label="Kind" />
|
||||
<.input field={f[:url]} type="text" label="Url" />
|
||||
<.input
|
||||
field={f[:channels]}
|
||||
type="select"
|
||||
label="Channel"
|
||||
multiple={true}
|
||||
options={@channels}
|
||||
value={selected_channels(@changeset)}
|
||||
/>
|
||||
<:actions>
|
||||
<.button>Save Note</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
|
@ -0,0 +1,20 @@
|
|||
<.header>
|
||||
Note <%= @note.id %>
|
||||
<:subtitle>This is a note record from your database.</:subtitle>
|
||||
<:actions>
|
||||
<.link href={~p"/notes/#{@note}/edit"}>
|
||||
<.button>Edit note</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.list>
|
||||
<:item title="Name"><%= @note.name %></:item>
|
||||
<:item title="Content"><%= @note.content %></:item>
|
||||
<:item title="Slug"><%= @note.slug %></:item>
|
||||
<:item title="Published at"><%= @note.published_at %></:item>
|
||||
<:item title="Kind"><%= @note.kind %></:item>
|
||||
<:item title="Url"><%= @note.url %></:item>
|
||||
</.list>
|
||||
|
||||
<.back navigate={~p"/notes"}>Back to notes</.back>
|
|
@ -21,6 +21,9 @@ defmodule ChiyaWeb.Router do
|
|||
pipe_through :browser
|
||||
|
||||
get "/", PageController, :home
|
||||
|
||||
resources "/channels", ChannelController
|
||||
resources "/notes", NoteController
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
|
|
3
mix.exs
3
mix.exs
|
@ -50,7 +50,8 @@ defmodule Chiya.MixProject do
|
|||
{:telemetry_poller, "~> 1.0"},
|
||||
{:gettext, "~> 0.20"},
|
||||
{:jason, "~> 1.2"},
|
||||
{:plug_cowboy, "~> 2.5"}
|
||||
{:plug_cowboy, "~> 2.5"},
|
||||
{:oban, "~> 2.14"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
1
mix.lock
1
mix.lock
|
@ -23,6 +23,7 @@
|
|||
"mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"},
|
||||
"nimble_options": {:hex, :nimble_options, "0.5.2", "42703307b924880f8c08d97719da7472673391905f528259915782bb346e0a1b", [:mix], [], "hexpm", "4da7f904b915fd71db549bcdc25f8d56f378ef7ae07dc1d372cbe72ba950dce0"},
|
||||
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
|
||||
"oban": {:hex, :oban, "2.14.2", "ae925d9a33e110addaa59ff7ec1b2fd84270ac7eb00fbb4b4a179d74c407bba3", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "32bf30127c8c44ac42f05f229a50fadc2177b3e799c29499f5daf90d5e5b5d3c"},
|
||||
"phoenix": {:hex, :phoenix, "1.7.1", "a029bde19d9c3b559e5c3d06c78b76e81396bedd456a6acedb42f9c7b2e535a9", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "ea9d4a85c3592e37efa07d0dc013254fda445885facaefddcbf646375c116457"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"},
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
defmodule Chiya.Repo.Migrations.AddObanJobsTable do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
Oban.Migration.up(version: 11)
|
||||
end
|
||||
|
||||
# We specify `version: 1` in `down`, ensuring that we'll roll all the way back down if
|
||||
# necessary, regardless of which version we've migrated `up` to.
|
||||
def down do
|
||||
Oban.Migration.down(version: 1)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
defmodule Chiya.Repo.Migrations.CreateChannels do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:channels) do
|
||||
add :name, :string
|
||||
add :content, :text
|
||||
add :visibility, :string
|
||||
add :slug, :string
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
create unique_index(:channels, [:slug])
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
defmodule Chiya.Repo.Migrations.CreateNotes do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:notes) do
|
||||
add :name, :string
|
||||
add :content, :text
|
||||
add :slug, :string
|
||||
add :published_at, :naive_datetime
|
||||
add :kind, :string
|
||||
add :url, :string
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
create unique_index(:notes, [:slug])
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
defmodule Chiya.Repo.Migrations.CreateChannelsNotes do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:channels_notes, primary_key: false) do
|
||||
add :channel, references(:channels, on_delete: :delete_all)
|
||||
add :note, references(:notes, on_delete: :delete_all)
|
||||
end
|
||||
|
||||
create index(:channels_notes, [:channel])
|
||||
create index(:channels_notes, [:note])
|
||||
end
|
||||
end
|
|
@ -1,9 +1,9 @@
|
|||
defmodule Chiya.AccountsTest do
|
||||
use Chiya.DataCase
|
||||
|
||||
alias Chiya.Accounts
|
||||
|
||||
import Chiya.AccountsFixtures
|
||||
|
||||
alias Chiya.Accounts
|
||||
alias Chiya.Accounts.{User, UserToken}
|
||||
|
||||
describe "get_user_by_email/1" do
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
defmodule Chiya.ChannelsTest do
|
||||
use Chiya.DataCase
|
||||
|
||||
import Chiya.ChannelsFixtures
|
||||
|
||||
alias Chiya.Channels
|
||||
alias Chiya.Channels.Channel
|
||||
|
||||
describe "channels" do
|
||||
@invalid_attrs %{content: nil, name: nil, slug: nil, visibility: nil}
|
||||
|
||||
test "list_channels/0 returns all channels" do
|
||||
channel = channel_fixture()
|
||||
assert Channels.list_channels() == [channel]
|
||||
end
|
||||
|
||||
test "get_channel!/1 returns the channel with given id" do
|
||||
channel = channel_fixture()
|
||||
assert Channels.get_channel!(channel.id) == channel
|
||||
end
|
||||
|
||||
test "create_channel/1 with valid data creates a channel" do
|
||||
valid_attrs = %{
|
||||
content: "some content",
|
||||
name: "some name",
|
||||
slug: "some slug",
|
||||
visibility: :public
|
||||
}
|
||||
|
||||
assert {:ok, %Channel{} = channel} = Channels.create_channel(valid_attrs)
|
||||
assert channel.content == "some content"
|
||||
assert channel.name == "some name"
|
||||
assert channel.slug == "some slug"
|
||||
assert channel.visibility == :public
|
||||
end
|
||||
|
||||
test "create_channel/1 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} = Channels.create_channel(@invalid_attrs)
|
||||
end
|
||||
|
||||
test "update_channel/2 with valid data updates the channel" do
|
||||
channel = channel_fixture()
|
||||
|
||||
update_attrs = %{
|
||||
content: "some updated content",
|
||||
name: "some updated name",
|
||||
slug: "some updated slug",
|
||||
visibility: :private
|
||||
}
|
||||
|
||||
assert {:ok, %Channel{} = channel} = Channels.update_channel(channel, update_attrs)
|
||||
assert channel.content == "some updated content"
|
||||
assert channel.name == "some updated name"
|
||||
assert channel.slug == "some updated slug"
|
||||
assert channel.visibility == :private
|
||||
end
|
||||
|
||||
test "update_channel/2 with invalid data returns error changeset" do
|
||||
channel = channel_fixture()
|
||||
assert {:error, %Ecto.Changeset{}} = Channels.update_channel(channel, @invalid_attrs)
|
||||
assert channel == Channels.get_channel!(channel.id)
|
||||
end
|
||||
|
||||
test "delete_channel/1 deletes the channel" do
|
||||
channel = channel_fixture()
|
||||
assert {:ok, %Channel{}} = Channels.delete_channel(channel)
|
||||
assert_raise Ecto.NoResultsError, fn -> Channels.get_channel!(channel.id) end
|
||||
end
|
||||
|
||||
test "change_channel/1 returns a channel changeset" do
|
||||
channel = channel_fixture()
|
||||
assert %Ecto.Changeset{} = Channels.change_channel(channel)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,83 @@
|
|||
defmodule Chiya.NotesTest do
|
||||
use Chiya.DataCase
|
||||
|
||||
import Chiya.NotesFixtures
|
||||
|
||||
alias Chiya.Notes
|
||||
alias Chiya.Notes.Note
|
||||
|
||||
describe "notes" do
|
||||
@invalid_attrs %{content: nil, kind: nil, name: nil, published_at: nil, slug: nil, url: nil}
|
||||
|
||||
test "list_notes/0 returns all notes" do
|
||||
note = note_fixture()
|
||||
assert Notes.list_notes() == [note]
|
||||
end
|
||||
|
||||
test "get_note!/1 returns the note with given id" do
|
||||
note = note_fixture()
|
||||
assert Notes.get_note_preloaded!(note.id) == note
|
||||
end
|
||||
|
||||
test "create_note/1 with valid data creates a note" do
|
||||
valid_attrs = %{
|
||||
content: "some content",
|
||||
kind: "some kind",
|
||||
name: "some name",
|
||||
published_at: ~N[2023-03-04 16:22:00],
|
||||
slug: "some slug",
|
||||
url: "some url"
|
||||
}
|
||||
|
||||
assert {:ok, %Note{} = note} = Notes.create_note(valid_attrs)
|
||||
assert note.content == "some content"
|
||||
assert note.kind == "some kind"
|
||||
assert note.name == "some name"
|
||||
assert note.published_at == ~N[2023-03-04 16:22:00]
|
||||
assert note.slug == "some slug"
|
||||
assert note.url == "some url"
|
||||
end
|
||||
|
||||
test "create_note/1 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} = Notes.create_note(@invalid_attrs)
|
||||
end
|
||||
|
||||
test "update_note/2 with valid data updates the note" do
|
||||
note = note_fixture()
|
||||
|
||||
update_attrs = %{
|
||||
content: "some updated content",
|
||||
kind: "some updated kind",
|
||||
name: "some updated name",
|
||||
published_at: ~N[2023-03-05 16:22:00],
|
||||
slug: "some updated slug",
|
||||
url: "some updated url"
|
||||
}
|
||||
|
||||
assert {:ok, %Note{} = note} = Notes.update_note(note, update_attrs)
|
||||
assert note.content == "some updated content"
|
||||
assert note.kind == "some updated kind"
|
||||
assert note.name == "some updated name"
|
||||
assert note.published_at == ~N[2023-03-05 16:22:00]
|
||||
assert note.slug == "some updated slug"
|
||||
assert note.url == "some updated url"
|
||||
end
|
||||
|
||||
test "update_note/2 with invalid data returns error changeset" do
|
||||
note = note_fixture()
|
||||
assert {:error, %Ecto.Changeset{}} = Notes.update_note(note, @invalid_attrs)
|
||||
assert note == Notes.get_note_preloaded!(note.id)
|
||||
end
|
||||
|
||||
test "delete_note/1 deletes the note" do
|
||||
note = note_fixture()
|
||||
assert {:ok, %Note{}} = Notes.delete_note(note)
|
||||
assert_raise Ecto.NoResultsError, fn -> Notes.get_note!(note.id) end
|
||||
end
|
||||
|
||||
test "change_note/1 returns a note changeset" do
|
||||
note = note_fixture()
|
||||
assert %Ecto.Changeset{} = Notes.change_note(note)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,94 @@
|
|||
defmodule ChiyaWeb.ChannelControllerTest do
|
||||
use ChiyaWeb.ConnCase
|
||||
|
||||
import Chiya.ChannelsFixtures
|
||||
|
||||
@create_attrs %{
|
||||
content: "some content",
|
||||
name: "some name",
|
||||
slug: "some slug",
|
||||
visibility: :public
|
||||
}
|
||||
@update_attrs %{
|
||||
content: "some updated content",
|
||||
name: "some updated name",
|
||||
slug: "some updated slug",
|
||||
visibility: :private
|
||||
}
|
||||
@invalid_attrs %{content: nil, name: nil, slug: nil, visibility: nil}
|
||||
|
||||
describe "index" do
|
||||
test "lists all channels", %{conn: conn} do
|
||||
conn = get(conn, ~p"/channels")
|
||||
assert html_response(conn, 200) =~ "Listing Channels"
|
||||
end
|
||||
end
|
||||
|
||||
describe "new channel" do
|
||||
test "renders form", %{conn: conn} do
|
||||
conn = get(conn, ~p"/channels/new")
|
||||
assert html_response(conn, 200) =~ "New Channel"
|
||||
end
|
||||
end
|
||||
|
||||
describe "create channel" do
|
||||
test "redirects to show when data is valid", %{conn: conn} do
|
||||
conn = post(conn, ~p"/channels", channel: @create_attrs)
|
||||
|
||||
assert %{id: id} = redirected_params(conn)
|
||||
assert redirected_to(conn) == ~p"/channels/#{id}"
|
||||
|
||||
conn = get(conn, ~p"/channels/#{id}")
|
||||
assert html_response(conn, 200) =~ "Channel #{id}"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn} do
|
||||
conn = post(conn, ~p"/channels", channel: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "New Channel"
|
||||
end
|
||||
end
|
||||
|
||||
describe "edit channel" do
|
||||
setup [:create_channel]
|
||||
|
||||
test "renders form for editing chosen channel", %{conn: conn, channel: channel} do
|
||||
conn = get(conn, ~p"/channels/#{channel}/edit")
|
||||
assert html_response(conn, 200) =~ "Edit Channel"
|
||||
end
|
||||
end
|
||||
|
||||
describe "update channel" do
|
||||
setup [:create_channel]
|
||||
|
||||
test "redirects when data is valid", %{conn: conn, channel: channel} do
|
||||
conn = put(conn, ~p"/channels/#{channel}", channel: @update_attrs)
|
||||
assert redirected_to(conn) == ~p"/channels/#{channel}"
|
||||
|
||||
conn = get(conn, ~p"/channels/#{channel}")
|
||||
assert html_response(conn, 200) =~ "some updated content"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn, channel: channel} do
|
||||
conn = put(conn, ~p"/channels/#{channel}", channel: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "Edit Channel"
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete channel" do
|
||||
setup [:create_channel]
|
||||
|
||||
test "deletes chosen channel", %{conn: conn, channel: channel} do
|
||||
conn = delete(conn, ~p"/channels/#{channel}")
|
||||
assert redirected_to(conn) == ~p"/channels"
|
||||
|
||||
assert_error_sent 404, fn ->
|
||||
get(conn, ~p"/channels/#{channel}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp create_channel(_) do
|
||||
channel = channel_fixture()
|
||||
%{channel: channel}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,141 @@
|
|||
defmodule ChiyaWeb.NoteControllerTest do
|
||||
use ChiyaWeb.ConnCase
|
||||
|
||||
import Chiya.NotesFixtures
|
||||
|
||||
@create_attrs %{
|
||||
content: "some content",
|
||||
kind: "some kind",
|
||||
name: "some name",
|
||||
published_at: ~N[2023-03-04 16:22:00],
|
||||
slug: "some slug",
|
||||
url: "some url"
|
||||
}
|
||||
@update_attrs %{
|
||||
content: "some updated content",
|
||||
kind: "some updated kind",
|
||||
name: "some updated name",
|
||||
published_at: ~N[2023-03-05 16:22:00],
|
||||
slug: "some updated slug",
|
||||
url: "some updated url"
|
||||
}
|
||||
@invalid_attrs %{content: nil, kind: nil, name: nil, published_at: nil, slug: nil, url: nil}
|
||||
|
||||
describe "index" do
|
||||
test "lists all notes", %{conn: conn} do
|
||||
conn = get(conn, ~p"/notes")
|
||||
assert html_response(conn, 200) =~ "Listing Notes"
|
||||
end
|
||||
end
|
||||
|
||||
describe "new note" do
|
||||
test "renders form", %{conn: conn} do
|
||||
conn = get(conn, ~p"/notes/new")
|
||||
assert html_response(conn, 200) =~ "New Note"
|
||||
end
|
||||
end
|
||||
|
||||
describe "create note" do
|
||||
test "redirects to show when data is valid", %{conn: conn} do
|
||||
conn = post(conn, ~p"/notes", note: @create_attrs)
|
||||
|
||||
assert %{id: id} = redirected_params(conn)
|
||||
assert redirected_to(conn) == ~p"/notes/#{id}"
|
||||
|
||||
conn = get(conn, ~p"/notes/#{id}")
|
||||
assert html_response(conn, 200) =~ "Note #{id}"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn} do
|
||||
conn = post(conn, ~p"/notes", note: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "New Note"
|
||||
end
|
||||
end
|
||||
|
||||
describe "create note with channel" do
|
||||
setup [:create_channels]
|
||||
|
||||
test "redirects to show when selecting a channel", %{conn: conn, channel: channel} do
|
||||
attrs = Map.put_new(@create_attrs, :channels, [to_string(channel.id)])
|
||||
conn = post(conn, ~p"/notes", note: attrs)
|
||||
|
||||
assert %{id: id} = redirected_params(conn)
|
||||
assert redirected_to(conn) == ~p"/notes/#{id}"
|
||||
|
||||
conn = get(conn, ~p"/notes/#{id}")
|
||||
assert html_response(conn, 200) =~ "Note #{id}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "edit note" do
|
||||
setup [:create_note]
|
||||
|
||||
test "renders form for editing chosen note", %{conn: conn, note: note} do
|
||||
conn = get(conn, ~p"/notes/#{note}/edit")
|
||||
assert html_response(conn, 200) =~ "Edit Note"
|
||||
end
|
||||
end
|
||||
|
||||
describe "update note" do
|
||||
setup [:create_note]
|
||||
|
||||
test "redirects when data is valid", %{conn: conn, note: note} do
|
||||
conn = put(conn, ~p"/notes/#{note}", note: @update_attrs)
|
||||
assert redirected_to(conn) == ~p"/notes/#{note}"
|
||||
|
||||
conn = get(conn, ~p"/notes/#{note}")
|
||||
assert html_response(conn, 200) =~ "some updated content"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn, note: note} do
|
||||
conn = put(conn, ~p"/notes/#{note}", note: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "Edit Note"
|
||||
end
|
||||
end
|
||||
|
||||
describe "update note with channel" do
|
||||
setup [:create_note, :create_channels]
|
||||
|
||||
test "adds and removes the correct channels from the note", %{
|
||||
conn: conn,
|
||||
note: note,
|
||||
channel: channel,
|
||||
channel2: channel2
|
||||
} do
|
||||
attrs = Map.put_new(@update_attrs, :channels, [to_string(channel.id)])
|
||||
conn = put(conn, ~p"/notes/#{note}", note: attrs)
|
||||
assert redirected_to(conn) == ~p"/notes/#{note}"
|
||||
|
||||
attrs = Map.put_new(@update_attrs, :channels, [to_string(channel2.id)])
|
||||
conn = put(conn, ~p"/notes/#{note}", note: attrs)
|
||||
assert redirected_to(conn) == ~p"/notes/#{note}"
|
||||
|
||||
conn = get(conn, ~p"/notes/#{note}")
|
||||
assert html_response(conn, 200) =~ "some updated content"
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete note" do
|
||||
setup [:create_note]
|
||||
|
||||
test "deletes chosen note", %{conn: conn, note: note} do
|
||||
conn = delete(conn, ~p"/notes/#{note}")
|
||||
assert redirected_to(conn) == ~p"/notes"
|
||||
|
||||
assert_error_sent 404, fn ->
|
||||
get(conn, ~p"/notes/#{note}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp create_note(_) do
|
||||
note = note_fixture()
|
||||
%{note: note}
|
||||
end
|
||||
|
||||
defp create_channels(_) do
|
||||
channel = Chiya.ChannelsFixtures.channel_fixture()
|
||||
channel2 = Chiya.ChannelsFixtures.channel_fixture()
|
||||
%{channel: channel, channel2: channel2}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Chiya.ChannelsFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `Chiya.Channels` context.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Generate a unique note slug.
|
||||
"""
|
||||
def unique_channel_slug, do: "some slug#{System.unique_integer([:positive])}"
|
||||
|
||||
@doc """
|
||||
Generate a channel.
|
||||
"""
|
||||
def channel_fixture(attrs \\ %{}) do
|
||||
{:ok, channel} =
|
||||
attrs
|
||||
|> Enum.into(%{
|
||||
content: "some content",
|
||||
name: "some name",
|
||||
slug: unique_channel_slug(),
|
||||
visibility: :public
|
||||
})
|
||||
|> Chiya.Channels.create_channel()
|
||||
|
||||
channel
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
defmodule Chiya.NotesFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `Chiya.Notes` context.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Generate a unique note slug.
|
||||
"""
|
||||
def unique_note_slug, do: "some slug#{System.unique_integer([:positive])}"
|
||||
|
||||
@doc """
|
||||
Generate a note.
|
||||
"""
|
||||
def note_fixture(attrs \\ %{}) do
|
||||
{:ok, note} =
|
||||
attrs
|
||||
|> Enum.into(%{
|
||||
content: "some content",
|
||||
kind: "some kind",
|
||||
name: "some name",
|
||||
published_at: ~N[2023-03-04 16:22:00],
|
||||
slug: unique_note_slug(),
|
||||
url: "some url"
|
||||
})
|
||||
|> Chiya.Notes.create_note()
|
||||
|
||||
note
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue