Skip to content

Commit 7f9124a

Browse files
Merge pull request #95 from epochtalk/post-preview
Post preview
2 parents 68b1d41 + c9434ec commit 7f9124a

File tree

6 files changed

+96
-1
lines changed

6 files changed

+96
-1
lines changed

lib/epochtalk_server_web/controllers/post.ex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,22 @@ defmodule EpochtalkServerWeb.Controllers.Post do
358358
end
359359
end
360360

361+
@doc """
362+
Get `Post` preview by running content through parser
363+
"""
364+
def preview(conn, attrs) do
365+
with post_max_length <-
366+
Map.get(Application.get_env(:epochtalk_server, :frontend_config), :post_max_length),
367+
body <-
368+
Validate.cast(attrs, "body", :string, required: true, max: post_max_length, min: 1),
369+
parsed_body <- Parse.markdown(body) do
370+
render(conn, :preview, %{parsed_body: parsed_body})
371+
else
372+
_ ->
373+
ErrorHelpers.render_json_error(conn, 400, "Error, cannot generate preview")
374+
end
375+
end
376+
361377
## === Private Authorization Helper Functions ===
362378

363379
defp can_authed_user_view_deleted_posts(nil, _thread_id), do: false

lib/epochtalk_server_web/helpers/parse.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,11 @@ defmodule EpochtalkServerWeb.Helpers.Parse do
99
@spec markdown_within_body(attrs :: map) :: map()
1010
def markdown_within_body(attrs) when is_map(attrs),
1111
do: Map.put(attrs, "body_html", Earmark.as_html!(attrs["body_html"] || attrs["body"]))
12+
13+
@doc """
14+
Used to parse markdown inorder to preview `Thread` or `Post` content from the frontend client
15+
"""
16+
@spec markdown_within_body(to_parse :: String.t()) :: String.t()
17+
def markdown(to_parse) when is_binary(to_parse),
18+
do: Earmark.as_html!(to_parse)
1219
end

lib/epochtalk_server_web/json/post_json.ex

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@ defmodule EpochtalkServerWeb.Controllers.PostJSON do
2525
post_data
2626
end
2727

28+
@doc """
29+
Renders `Post` data for preview purposes
30+
31+
## Example
32+
iex> parsed_body = %{parsed_body: "<p><strong>Hello World</strong><p>"}
33+
iex> EpochtalkServerWeb.Controllers.PostJSON.preview(parsed_body)
34+
parsed_body
35+
"""
36+
def preview(%{
37+
parsed_body: parsed_body
38+
}) do
39+
%{parsed_body: parsed_body}
40+
end
41+
2842
@doc """
2943
Renders all `Post` for a particular `Thread`.
3044
"""

lib/epochtalk_server_web/router.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ defmodule EpochtalkServerWeb.Router do
5151
post "/threads/:thread_id/polls/lock", Poll, :lock
5252
put "/threads/:thread_id/polls", Poll, :update
5353
post "/threads/:thread_id/polls", Poll, :create
54-
get "/posts", Post, :by_thread
5554
get "/posts/draft", PostDraft, :by_user_id
5655
put "/posts/draft", PostDraft, :upsert
5756
post "/posts", Post, :create
5857
post "/posts/:id", Post, :update
58+
post "/preview", Post, :preview
5959
get "/admin/modlog", ModerationLog, :page
6060
get "/boards/movelist", Board, :movelist
6161
end
@@ -66,6 +66,7 @@ defmodule EpochtalkServerWeb.Router do
6666
get "/boards/:id", Board, :find
6767
get "/boards/:slug/id", Board, :slug_to_id
6868
get "/breadcrumbs", Breadcrumb, :breadcrumbs
69+
get "/posts", Post, :by_thread
6970
get "/threads", Thread, :by_board
7071
get "/threads/:slug/id", Thread, :slug_to_id
7172
post "/threads/:id/viewed", Thread, :viewed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
defmodule Test.EpochtalkServerWeb.Controllers.Post do
2+
use Test.Support.ConnCase, async: true
3+
alias EpochtalkServerWeb.CustomErrors.InvalidPayload
4+
5+
describe "preview/1" do
6+
test "when unauthenticated, returns Unauthorized error", %{conn: conn} do
7+
response =
8+
conn
9+
|> post(Routes.post_path(conn, :preview), %{"body" => "**Test**"})
10+
|> json_response(401)
11+
12+
assert response["error"] == "Unauthorized"
13+
assert response["message"] == "No resource found"
14+
end
15+
16+
@tag :authenticated
17+
test "when authenticated and supplied with markdown string for body, returns html string",
18+
%{conn: conn} do
19+
response =
20+
conn
21+
|> post(Routes.post_path(conn, :preview), %{"body" => "**Test**"})
22+
|> json_response(200)
23+
24+
assert response["parsed_body"] == "<p>\n<strong>Test</strong></p>\n"
25+
end
26+
27+
@tag :authenticated
28+
test "when authenticated and supplied with empty string for body, returns error",
29+
%{conn: conn} do
30+
assert_raise InvalidPayload,
31+
~r/^Invalid payload, key 'body' should be of type 'string' with length between 1 and 10000/,
32+
fn ->
33+
post(conn, Routes.post_path(conn, :preview), %{"body" => ""})
34+
end
35+
end
36+
37+
@tag :authenticated
38+
test "when authenticated and supplied with too large of a string for body, returns error",
39+
%{conn: conn} do
40+
assert_raise InvalidPayload,
41+
~r/^Invalid payload, key 'body' should be of type 'string' with length between 1 and 10000/,
42+
fn ->
43+
post(conn, Routes.post_path(conn, :preview), %{
44+
"body" =>
45+
for(_ <- 1..10_001, into: "", do: <<Enum.random('0123456789abcdef')>>)
46+
})
47+
end
48+
end
49+
end
50+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
defmodule Test.EpochtalkServerWeb.Controllers.PostJSON do
2+
use Test.Support.ConnCase, async: true
3+
alias EpochtalkServerWeb.Controllers.PostJSON
4+
5+
# Specify that we want to use doctests:
6+
doctest PostJSON
7+
end

0 commit comments

Comments
 (0)