diff --git a/priv/repo/migrations/20160815125009_create_preview.exs b/priv/repo/migrations/20160815125009_create_preview.exs new file mode 100644 index 000000000..ce8891521 --- /dev/null +++ b/priv/repo/migrations/20160815125009_create_preview.exs @@ -0,0 +1,14 @@ +defmodule CodeCorps.Repo.Migrations.CreatePreview do + use Ecto.Migration + + def change do + create table(:preview) do + add :markdown, :text, null: false + add :body, :text, null: false + + add :user_id, references(:users, on_delete: :nothing) + + timestamps() + end + end +end diff --git a/test/controllers/preview_controller_test.exs b/test/controllers/preview_controller_test.exs new file mode 100644 index 000000000..5d5213a3c --- /dev/null +++ b/test/controllers/preview_controller_test.exs @@ -0,0 +1,72 @@ +defmodule CodeCorps.PreviewControllerTest do + use CodeCorps.ConnCase + + alias CodeCorps.Preview + + setup do + conn = + %{build_conn | host: "api."} + |> put_req_header("accept", "application/vnd.api+json") + |> put_req_header("content-type", "application/vnd.api+json") + + {:ok, conn: conn} + end + + test "creates and renders resource, with body containing markdown rendered to html", %{conn: conn} do + conn = post conn, preview_path(conn, :create), %{ + "meta" => %{}, + "data" => %{"type" => "preview","attributes" => %{markdown: "A **strong** element"}} + } + + json = + conn + |> json_response(201) + + id = + json["data"]["id"] + |> String.to_integer + + assert id + + attributes = json["data"]["attributes"] + + assert attributes["body"] == "
A strong element
\n" + assert attributes["markdown"] == "A **strong** element" + + preview = + Preview + |> Repo.get!(id) + + assert preview.body == "A strong element
\n" + assert preview.markdown == "A **strong** element" + end + + test "it assigns current user as owner of preview, if available", %{conn: conn} do + user = insert_user() + + path = preview_path(conn, :create) + payload = %{ + "meta" => %{}, + "data" => %{"type" => "preview", "attributes" => %{markdown: "A **strong** element"}} + } + + conn = + conn + |> Guardian.Plug.api_sign_in(user) + |> post(path, payload) + + json = + conn + |> json_response(201) + + id = + json["data"]["id"] + |> String.to_integer + + preview = + Preview + |> Repo.get!(id) + + assert preview.user_id == user.id + end +end diff --git a/test/models/preview_test.exs b/test/models/preview_test.exs new file mode 100644 index 000000000..adcb13dc9 --- /dev/null +++ b/test/models/preview_test.exs @@ -0,0 +1,24 @@ +defmodule CodeCorps.PreviewTest do + use CodeCorps.ModelCase + + alias CodeCorps.Preview + + test "changeset renders body html from markdown" do + changeset = Preview.changeset(%Preview{}, %{markdown: "A **strong** element"}, nil) + assert changeset.valid? + assert changeset |> get_change(:body) == "A strong element
\n" + end + + test "changeset requires markdown change" do + changeset = Preview.changeset(%Preview{}, %{}, nil) + refute changeset.valid? + assert changeset.errors[:markdown] == {"can't be blank", []} + end + + test "assings user_id if present" do + user = insert_user + changeset = Preview.changeset(%Preview{}, %{markdown: "A **strong** element"}, user) + assert changeset.valid? + assert Ecto.Changeset.get_change(changeset, :user).data == user + end +end diff --git a/web/controllers/preview_controller.ex b/web/controllers/preview_controller.ex new file mode 100644 index 000000000..44607d46f --- /dev/null +++ b/web/controllers/preview_controller.ex @@ -0,0 +1,31 @@ +defmodule CodeCorps.PreviewController do + use CodeCorps.Web, :controller + + alias CodeCorps.Preview + alias JaSerializer.Params + + def create(conn, %{"data" => data = %{"type" => "preview", "attributes" => _project_params}}) do + user = + conn + |> Guardian.Plug.current_resource + + changeset = Preview.changeset(%Preview{}, Params.to_attributes(data), user) + + + + case Repo.insert(changeset) do + {:ok, preview} -> + preview = + preview + |> Repo.preload([:user]) + + conn + |> put_status(:created) + |> render("show.json-api", data: preview) + {:error, changeset} -> + conn + |> put_status(:unprocessable_entity) + |> render(CodeCorps.ChangesetView, "error.json-api", changeset: changeset) + end + end +end diff --git a/web/models/preview.ex b/web/models/preview.ex new file mode 100644 index 000000000..db9451f20 --- /dev/null +++ b/web/models/preview.ex @@ -0,0 +1,43 @@ +defmodule CodeCorps.Preview do + @moduledoc """ + Represents an category on Code Corps, e.g. "Society" and "Technology". + """ + + use CodeCorps.Web, :model + + schema "preview" do + field :body, :string + field :markdown, :string + + belongs_to :user, CodeCorps.User + + timestamps() + end + + @doc """ + Builds a changeset based on the `struct` and `params`. + """ + def changeset(struct, params \\ %{}, user) do + struct + |> cast(params, [:markdown]) + |> validate_required([:markdown]) + |> assign_user(user) + |> render_markdown_to_html + end + + defp render_markdown_to_html(changeset = %Ecto.Changeset{changes: %{markdown: markdown}}) do + html = + markdown + |> Earmark.to_html + + changeset + |> put_change(:body, html) + end + defp render_markdown_to_html(changeset), do: changeset + + defp assign_user(changeset, nil), do: changeset + defp assign_user(changeset, user) do + changeset + |> put_assoc(:user, user) + end +end diff --git a/web/router.ex b/web/router.ex index fc4aec8e3..9a02f67a3 100644 --- a/web/router.ex +++ b/web/router.ex @@ -38,6 +38,8 @@ defmodule CodeCorps.Router do resources "/projects", ProjectController, except: [:delete] + resources "/previews", PreviewController, only: [:create] + get "/:slug", SluggedRouteController, :show get "/:slug/projects", ProjectController, :index get "/:slug/:project_slug", ProjectController, :show diff --git a/web/views/preview_view.ex b/web/views/preview_view.ex new file mode 100644 index 000000000..aeccaf84f --- /dev/null +++ b/web/views/preview_view.ex @@ -0,0 +1,8 @@ +defmodule CodeCorps.PreviewView do + use CodeCorps.Web, :view + use JaSerializer.PhoenixView + + attributes [:markdown, :body, :inserted_at, :updated_at] + + has_one :user, serializer: CodeCorps.UserView +end