diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..b77055f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,67 @@ +{ + "version": "2.0.0", + "tasks": [{ + "label": "mix test", + "type": "shell", + "command": "mix", + "args": [ + "test", + "--exclude", + "integration", + "--exclude", + "feature", + "--color" + ], + "options": { + "cwd": "${workspaceRoot}", + "requireFiles": [ + "test/**/test_helper.exs", + "test/**/*_test.exs" + ] + }, + "problemMatcher": "$mixTestFailure" + }, + { + "label": "mix test file", + "type": "shell", + "command": "mix", + "args": [ + "test", + "${relativeFile}", + "--color", + "--trace" + ], + "options": { + "cwd": "${workspaceRoot}", + "requireFiles": [ + "test/**/test_helper.exs", + "test/**/*_test.exs" + ] + }, + "problemMatcher": "$mixTestFailure" + }, + { + "label": "mix test focused", + "type": "shell", + "command": "mix", + "args": [ + "test", + "${relativeFile}:${lineNumber}", + "--color", + "--trace" + ], + "options": { + "cwd": "${workspaceRoot}", + "requireFiles": [ + "test/**/test_helper.exs", + "test/**/*_test.exs" + ] + }, + "problemMatcher": "$mixTestFailure", + "group": { + "kind": "test", + "isDefault": true + } + } + ] + } \ No newline at end of file diff --git a/config/config.exs b/config/config.exs index dc21086..bee4c18 100644 --- a/config/config.exs +++ b/config/config.exs @@ -10,6 +10,8 @@ use Mix.Config config :entendu, ecto_repos: [Entendu.Repo] +config :entendu, Entendu.Repo, migration_primary_key: [type: :uuid] + # Configures the endpoint config :entendu, EntenduWeb.Endpoint, url: [host: "dev.intended.link"], diff --git a/lib/entendu/links/link.ex b/lib/entendu/links/link.ex index 4df3a9a..d939945 100644 --- a/lib/entendu/links/link.ex +++ b/lib/entendu/links/link.ex @@ -2,6 +2,8 @@ defmodule Entendu.Links.Link do use Ecto.Schema import Ecto.Changeset + @primary_key {:id, Ecto.UUID, autogenerate: true} + schema "links" do field :burn_after_reading, :boolean, default: false field :expires, :utc_datetime @@ -16,7 +18,13 @@ defmodule Entendu.Links.Link do @doc false def changeset(link, attrs) do link - |> cast(attrs, [:expires, :burn_after_reading, :filename, :filetype, :text_content, :file_content]) - |> validate_required([:expires, :burn_after_reading, :filename, :filetype]) + |> cast(attrs, [ + :expires, + :burn_after_reading, + :filename, + :filetype, + :text_content, + :file_content + ]) end end diff --git a/lib/entendu_web/controllers/fallback_controller.ex b/lib/entendu_web/controllers/fallback_controller.ex new file mode 100644 index 0000000..26367d8 --- /dev/null +++ b/lib/entendu_web/controllers/fallback_controller.ex @@ -0,0 +1,10 @@ +defmodule EntenduWeb.FallbackController do + use EntenduWeb, :controller + + def call(conn, {:error, %Ecto.Changeset{} = changeset}) do + conn + |> put_status(:unprocessable_entity) + |> put_view(EntenduWeb.ChangesetView) + |> render("error.json", changeset: changeset) + end +end diff --git a/lib/entendu_web/controllers/link_controller.ex b/lib/entendu_web/controllers/link_controller.ex index 04e2e2e..f804274 100644 --- a/lib/entendu_web/controllers/link_controller.ex +++ b/lib/entendu_web/controllers/link_controller.ex @@ -9,27 +9,33 @@ defmodule EntenduWeb.LinkController do alias Entendu.Links alias Links.Link alias Ecto.Changeset + alias EntenduWeb.FallbackController + + action_fallback(FallbackController) def just_page(conn, _params) do render(conn, "just.html") end - defparams first_step %{ - burn_after_reading: [field: :boolean, default: false], - expires: :utc_datetime, - filename: :string, - filetype: :string, - text_content: :string, - file_content: :string - } + defparams( + first_step(%{ + burn_after_reading: [field: :boolean, default: false], + expires: :utc_datetime, + filename: :string, + filetype: :string, + text_content: :string, + file_content: :string + }) + ) def just(conn, params) do with %Changeset{valid?: true} = changeset <- first_step(params), - link_params <- Params.to_map(changeset), - {:ok, %Link{} = link} <- Links.create_link(link_params) do + link_params <- Params.to_map(changeset), + {:ok, %Link{} = link} <- Links.create_link(link_params) do conn + |> put_status(:created) |> assign(:link, link) - |> render("show_authorized", link: link) + |> render("show_authorized.json", %{link: link}) end end diff --git a/lib/entendu_web/views/changeset_view.ex b/lib/entendu_web/views/changeset_view.ex new file mode 100644 index 0000000..99048dd --- /dev/null +++ b/lib/entendu_web/views/changeset_view.ex @@ -0,0 +1,15 @@ +defmodule EntenduWeb.ChangesetView do + use EntenduWeb, :view + + def translate_errors(%Ecto.Changeset{} = changeset) do + Ecto.Changeset.traverse_errors(changeset, &translate_error/1) + end + + def translate_errors(errors = %{}), do: errors + + def render("error.json", %{changeset: changeset}) do + # When encoded, the changeset returns its errors + # as a JSON object. So we just pass it forward. + %{errors: translate_errors(changeset)} + end +end diff --git a/test/entendu_web/controllers/links_controller_test.exs b/test/entendu_web/controllers/links_controller_test.exs new file mode 100644 index 0000000..1b269c0 --- /dev/null +++ b/test/entendu_web/controllers/links_controller_test.exs @@ -0,0 +1,27 @@ +defmodule EntenduWeb.ErrorViewTest do + use EntenduWeb.ConnCase, async: true + + # Bring render/3 and render_to_string/3 for testing custom views + import Phoenix.View + + test "renders 404.html" do + assert render_to_string(EntenduWeb.ErrorView, "404.html", []) == "Not Found" + end + + test "just endpoint creates a link with valid attrs", %{conn: conn} do + params = %{ + "text_content" => "some gibberish", + "filename" => "more gibberish" + } + + conn = post(conn, Routes.link_path(conn, :just), params) + + response = json_response(conn, 201) + + assert %{ + "id" => _link_id, + "text_content" => "some gibberish", + "filename" => "more gibberish" + } = response + end +end