-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #92 from epochtalk/post-drafts
Post drafts
- Loading branch information
Showing
16 changed files
with
298 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
defmodule EpochtalkServer.Models.PostDraft do | ||
use Ecto.Schema | ||
import Ecto.Query | ||
import Ecto.Changeset | ||
alias EpochtalkServer.Repo | ||
alias EpochtalkServer.Models.User | ||
alias EpochtalkServer.Models.PostDraft | ||
|
||
@moduledoc """ | ||
`PostDraft` model, for performing actions relating to a `User`'s `Post` draft | ||
""" | ||
@type t :: %__MODULE__{ | ||
user_id: non_neg_integer | nil, | ||
draft: String.t() | nil, | ||
updated_at: NaiveDateTime.t() | nil | ||
} | ||
@derive {Jason.Encoder, | ||
only: [ | ||
:user_id, | ||
:draft, | ||
:updated_at | ||
]} | ||
@schema_prefix "posts" | ||
@primary_key false | ||
schema "user_drafts" do | ||
belongs_to :user, User | ||
field :draft, :string | ||
field :updated_at, :naive_datetime | ||
end | ||
|
||
## === Changeset Functions === | ||
|
||
@doc """ | ||
Create a changeset for upserting a `PostDraft` | ||
""" | ||
@spec upsert_changeset(draft :: PostDraft.t(), attrs :: map() | nil) :: Ecto.Changeset.t() | ||
def upsert_changeset(draft, attrs \\ %{}) do | ||
updated_at = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) | ||
|
||
draft = | ||
draft | ||
|> Map.put(:updated_at, updated_at) | ||
|
||
draft | ||
|> cast(attrs, [:user_id, :draft, :updated_at]) | ||
|> validate_required([:user_id, :updated_at]) | ||
|> validate_length(:draft, min: 1, max: 64_000) | ||
|> unique_constraint(:user_id, name: :user_drafts_user_id_index) | ||
|> foreign_key_constraint(:user_id, name: :user_drafts_user_id_fkey) | ||
end | ||
|
||
## === Database Functions === | ||
|
||
@doc """ | ||
Returns `PostDraft` Data given a `User` id | ||
""" | ||
@spec by_user_id(user_id :: integer) :: t() | nil | ||
def by_user_id(user_id) do | ||
query = | ||
from p in PostDraft, | ||
where: p.user_id == ^user_id | ||
|
||
Repo.one(query) | ||
end | ||
|
||
@doc """ | ||
Used to upsert a `PostDraft` for a specific `User` | ||
""" | ||
@spec upsert(user_id :: non_neg_integer, attrs :: map()) :: | ||
{:ok, t()} | {:error, Ecto.Changeset.t()} | ||
def upsert(user_id, attrs) when is_integer(user_id) do | ||
post_draft_cs = upsert_changeset(%PostDraft{user_id: user_id}, attrs) | ||
|
||
Repo.insert( | ||
post_draft_cs, | ||
on_conflict: [ | ||
set: [ | ||
draft: Map.get(post_draft_cs.changes, :draft), | ||
updated_at: Map.get(post_draft_cs.data, :updated_at) | ||
] | ||
], | ||
conflict_target: [:user_id] | ||
) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
defmodule EpochtalkServerWeb.Controllers.PostDraft do | ||
use EpochtalkServerWeb, :controller | ||
|
||
@moduledoc """ | ||
Controller for `PostDraft` related API requests | ||
""" | ||
alias EpochtalkServer.Auth.Guardian | ||
alias EpochtalkServerWeb.ErrorHelpers | ||
alias EpochtalkServer.Models.PostDraft | ||
|
||
@doc """ | ||
Used to upsert a specific `PostDraft` | ||
Returns `PostDraft` on success | ||
""" | ||
def upsert(conn, attrs) do | ||
with {:auth, %{} = user} <- {:auth, Guardian.Plug.current_resource(conn)}, | ||
{:ok, draft_data} <- PostDraft.upsert(user.id, attrs) do | ||
render(conn, :upsert, %{draft_data: draft_data, user_id: user.id}) | ||
else | ||
{:auth, nil} -> | ||
ErrorHelpers.render_json_error(conn, 403, "Not logged in, cannot upsert post draft") | ||
|
||
{:error, data} -> | ||
ErrorHelpers.render_json_error(conn, 400, data) | ||
end | ||
end | ||
|
||
@doc """ | ||
Get `PostDraft` by `User` id | ||
""" | ||
def by_user_id(conn, _) do | ||
with {:auth, %{} = user} <- {:auth, Guardian.Plug.current_resource(conn)}, | ||
draft_data <- PostDraft.by_user_id(user.id) do | ||
render(conn, :by_user_id, %{draft_data: draft_data, user_id: user.id}) | ||
else | ||
{:auth, nil} -> | ||
ErrorHelpers.render_json_error(conn, 403, "Not logged in, cannot get post draft") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
defmodule EpochtalkServerWeb.Controllers.PostDraftJSON do | ||
@moduledoc """ | ||
Renders and formats `PostDraft` data, in JSON format for frontend | ||
""" | ||
|
||
@doc """ | ||
Renders `PostDraft` upsert result data in JSON | ||
## Example | ||
iex> draft_data = %{ | ||
iex> user_id: 99, | ||
iex> draft: "Hello world", | ||
iex> updated_at: ~N[2024-03-12 01:28:15] | ||
iex> } | ||
iex> EpochtalkServerWeb.Controllers.PostDraftJSON.upsert(%{draft_data: draft_data}) | ||
draft_data | ||
""" | ||
def upsert(%{draft_data: draft_data}), do: draft_data | ||
|
||
@doc """ | ||
Renders `PostDraft` by_user_id result data in JSON | ||
## Example | ||
iex> EpochtalkServerWeb.Controllers.PostDraftJSON.by_user_id(%{draft_data: nil, user_id: 98}) | ||
%{user_id: 98, draft: nil, updated_at: nil} | ||
iex> draft_data = %{ | ||
iex> user_id: 98, | ||
iex> draft: "Hello world", | ||
iex> updated_at: ~N[2024-03-12 01:28:15] | ||
iex> } | ||
iex> EpochtalkServerWeb.Controllers.PostDraftJSON.by_user_id(%{draft_data: draft_data, user_id: 98}) | ||
draft_data | ||
""" | ||
def by_user_id(%{draft_data: nil, user_id: user_id}), | ||
do: %{user_id: user_id, draft: nil, updated_at: nil} | ||
|
||
def by_user_id(%{draft_data: draft_data, user_id: _user_id}), do: draft_data | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
defmodule Test.EpochtalkServerWeb.Controllers.PostDraft do | ||
use Test.Support.ConnCase, async: true | ||
import Test.Support.Factory | ||
|
||
setup %{users: %{admin_user: admin_user}} do | ||
draft_data = | ||
build(:post_draft, %{ | ||
user_id: admin_user.id, | ||
draft: "draft test" | ||
}) | ||
|
||
{:ok, draft_data: draft_data} | ||
end | ||
|
||
describe "by_user_id/1" do | ||
test "when unauthenticated, returns Unauthorized error", %{conn: conn} do | ||
response = | ||
conn | ||
|> get(Routes.post_draft_path(conn, :by_user_id)) | ||
|> json_response(403) | ||
|
||
assert response["error"] == "Forbidden" | ||
assert response["message"] == "Not logged in, cannot get post draft" | ||
end | ||
|
||
@tag :authenticated | ||
test "when authenticated with no existing post draft, returns empty post draft", | ||
%{conn: conn, users: %{user: user}} do | ||
response = | ||
conn | ||
|> get(Routes.post_draft_path(conn, :by_user_id)) | ||
|> json_response(200) | ||
|
||
assert response["user_id"] == user.id | ||
assert response["draft"] == nil | ||
assert response["updated_at"] == nil | ||
end | ||
|
||
@tag authenticated: :admin | ||
test "when authenticated with an existing post draft, returns existing post draft", | ||
%{conn: conn, users: %{admin_user: admin_user}, draft_data: draft_data} do | ||
response = | ||
conn | ||
|> get(Routes.post_draft_path(conn, :by_user_id)) | ||
|> json_response(200) | ||
|
||
assert response["user_id"] == admin_user.id | ||
assert response["draft"] == draft_data.draft | ||
assert response["updated_at"] != nil | ||
end | ||
end | ||
|
||
describe "upsert/1" do | ||
test "when unauthenticated, returns Unauthorized error", %{conn: conn} do | ||
response = | ||
conn | ||
|> put(Routes.post_draft_path(conn, :upsert), %{"draft" => "Hello World"}) | ||
|> json_response(403) | ||
|
||
assert response["error"] == "Forbidden" | ||
assert response["message"] == "Not logged in, cannot upsert post draft" | ||
end | ||
|
||
@tag :authenticated | ||
test "when authenticated with no existing post draft, creates and returns new post draft", | ||
%{conn: conn, users: %{user: user}} do | ||
response = | ||
conn | ||
|> put(Routes.post_draft_path(conn, :upsert), %{"draft" => "Hello World"}) | ||
|> json_response(200) | ||
|
||
assert response["user_id"] == user.id | ||
assert response["draft"] == "Hello World" | ||
assert response["updated_at"] != nil | ||
end | ||
|
||
@tag authenticated: :admin | ||
test "when authenticated with an existing post draft, overrides existing and returns new post draft", | ||
%{conn: conn, users: %{admin_user: admin_user}, draft_data: draft_data} do | ||
response = | ||
conn | ||
|> put(Routes.post_draft_path(conn, :upsert), %{"draft" => "Hello World"}) | ||
|> json_response(200) | ||
|
||
assert response["user_id"] == admin_user.id | ||
assert response["draft"] != draft_data.draft | ||
assert response["draft"] == "Hello World" | ||
assert response["updated_at"] != nil | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
defmodule Test.EpochtalkServerWeb.Controllers.PostDraftJSON do | ||
use Test.Support.ConnCase, async: true | ||
alias EpochtalkServerWeb.Controllers.PostDraftJSON | ||
|
||
# Specify that we want to use doctests: | ||
doctest PostDraftJSON | ||
end |
Oops, something went wrong.