Skip to content

Commit 3a93757

Browse files
committed
Add Previews API
1 parent 5176eaf commit 3a93757

File tree

7 files changed

+194
-0
lines changed

7 files changed

+194
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
defmodule CodeCorps.Repo.Migrations.CreatePreview do
2+
use Ecto.Migration
3+
4+
def change do
5+
create table(:preview) do
6+
add :markdown, :text, null: false
7+
add :body, :text, null: false
8+
9+
add :user_id, references(:users, on_delete: :nothing)
10+
11+
timestamps()
12+
end
13+
end
14+
end
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
defmodule CodeCorps.PreviewControllerTest do
2+
use CodeCorps.ConnCase
3+
4+
alias CodeCorps.Preview
5+
6+
setup do
7+
conn =
8+
%{build_conn | host: "api."}
9+
|> put_req_header("accept", "application/vnd.api+json")
10+
|> put_req_header("content-type", "application/vnd.api+json")
11+
12+
{:ok, conn: conn}
13+
end
14+
15+
test "creates and renders resource, with body containing markdown rendered to html", %{conn: conn} do
16+
conn = post conn, preview_path(conn, :create), %{
17+
"meta" => %{},
18+
"data" => %{"type" => "preview","attributes" => %{markdown: "A **strong** element"}}
19+
}
20+
21+
json =
22+
conn
23+
|> json_response(201)
24+
25+
id =
26+
json["data"]["id"]
27+
|> String.to_integer
28+
29+
assert id
30+
31+
attributes = json["data"]["attributes"]
32+
33+
assert attributes["body"] == "<p>A <strong>strong</strong> element</p>\n"
34+
assert attributes["markdown"] == "A **strong** element"
35+
36+
preview =
37+
Preview
38+
|> Repo.get!(id)
39+
40+
assert preview.body == "<p>A <strong>strong</strong> element</p>\n"
41+
assert preview.markdown == "A **strong** element"
42+
end
43+
44+
test "it assigns current user as owner of preview, if available", %{conn: conn} do
45+
user = insert_user()
46+
47+
path = preview_path(conn, :create)
48+
payload = %{
49+
"meta" => %{},
50+
"data" => %{"type" => "preview", "attributes" => %{markdown: "A **strong** element"}}
51+
}
52+
53+
conn =
54+
conn
55+
|> Guardian.Plug.api_sign_in(user)
56+
|> post(path, payload)
57+
58+
json =
59+
conn
60+
|> json_response(201)
61+
62+
id =
63+
json["data"]["id"]
64+
|> String.to_integer
65+
66+
preview =
67+
Preview
68+
|> Repo.get!(id)
69+
70+
assert preview.user_id == user.id
71+
end
72+
end

test/models/preview_test.exs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
defmodule CodeCorps.PreviewTest do
2+
use CodeCorps.ModelCase
3+
4+
alias CodeCorps.Preview
5+
6+
test "changeset renders body html from markdown" do
7+
changeset = Preview.changeset(%Preview{}, %{markdown: "A **strong** element"}, nil)
8+
assert changeset.valid?
9+
assert changeset |> get_change(:body) == "<p>A <strong>strong</strong> element</p>\n"
10+
end
11+
12+
test "changeset requires markdown change" do
13+
changeset = Preview.changeset(%Preview{}, %{}, nil)
14+
refute changeset.valid?
15+
assert changeset.errors[:markdown] == {"can't be blank", []}
16+
end
17+
18+
test "assings user_id if present" do
19+
user = insert_user
20+
changeset = Preview.changeset(%Preview{}, %{markdown: "A **strong** element"}, user)
21+
assert changeset.valid?
22+
assert Ecto.Changeset.get_change(changeset, :user).data == user
23+
end
24+
end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
defmodule CodeCorps.PreviewController do
2+
use CodeCorps.Web, :controller
3+
4+
alias CodeCorps.Preview
5+
alias JaSerializer.Params
6+
7+
def create(conn, %{"data" => data = %{"type" => "preview", "attributes" => _project_params}}) do
8+
user =
9+
conn
10+
|> Guardian.Plug.current_resource
11+
12+
changeset = Preview.changeset(%Preview{}, Params.to_attributes(data), user)
13+
14+
15+
16+
case Repo.insert(changeset) do
17+
{:ok, preview} ->
18+
preview =
19+
preview
20+
|> Repo.preload([:user])
21+
22+
conn
23+
|> put_status(:created)
24+
|> render("show.json-api", data: preview)
25+
{:error, changeset} ->
26+
conn
27+
|> put_status(:unprocessable_entity)
28+
|> render(CodeCorps.ChangesetView, "error.json-api", changeset: changeset)
29+
end
30+
end
31+
end

web/models/preview.ex

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
defmodule CodeCorps.Preview do
2+
@moduledoc """
3+
Represents an category on Code Corps, e.g. "Society" and "Technology".
4+
"""
5+
6+
use CodeCorps.Web, :model
7+
8+
schema "preview" do
9+
field :body, :string
10+
field :markdown, :string
11+
12+
belongs_to :user, CodeCorps.User
13+
14+
timestamps()
15+
end
16+
17+
@doc """
18+
Builds a changeset based on the `struct` and `params`.
19+
"""
20+
def changeset(struct, params \\ %{}, user) do
21+
struct
22+
|> cast(params, [:markdown])
23+
|> validate_required([:markdown])
24+
|> assign_user(user)
25+
|> render_markdown_to_html
26+
end
27+
28+
defp render_markdown_to_html(changeset = %Ecto.Changeset{changes: %{markdown: markdown}}) do
29+
html =
30+
markdown
31+
|> Earmark.to_html
32+
33+
changeset
34+
|> put_change(:body, html)
35+
end
36+
defp render_markdown_to_html(changeset), do: changeset
37+
38+
defp assign_user(changeset, nil), do: changeset
39+
defp assign_user(changeset, user) do
40+
changeset
41+
|> put_assoc(:user, user)
42+
end
43+
end

web/router.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ defmodule CodeCorps.Router do
3838

3939
resources "/projects", ProjectController, except: [:delete]
4040

41+
resources "/previews", PreviewController, only: [:create]
42+
4143
get "/:slug", SluggedRouteController, :show
4244
get "/:slug/projects", ProjectController, :index
4345
get "/:slug/:project_slug", ProjectController, :show

web/views/preview_view.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
defmodule CodeCorps.PreviewView do
2+
use CodeCorps.Web, :view
3+
use JaSerializer.PhoenixView
4+
5+
attributes [:markdown, :body, :inserted_at, :updated_at]
6+
7+
has_one :user, serializer: CodeCorps.UserView
8+
end

0 commit comments

Comments
 (0)