From 783df8bffa3a0d02f012a948b9fb39958e9c2bd0 Mon Sep 17 00:00:00 2001 From: Andrew Walton Date: Fri, 9 Dec 2016 17:48:37 -0500 Subject: [PATCH 1/5] Added schema for tasks lists - #554 Also added: * task list filtering for tasks API * automatically create task lists on project creation - #553 Still remaining: * Implementing APIs and views * Implementing tests --- lib/code_corps/helpers/query.ex | 6 ++ mix.exs | 3 +- mix.lock | 9 +-- .../20161209192504_create_task_list.exs | 66 +++++++++++++++++++ .../controllers/task_list_controller_test.exs | 62 +++++++++++++++++ test/models/task_list_test.exs | 18 +++++ web/controllers/task_controller.ex | 3 +- web/controllers/task_list_controller.ex | 55 ++++++++++++++++ web/models/project.ex | 4 +- web/models/task.ex | 10 ++- web/models/task_list.ex | 43 ++++++++++++ web/router.ex | 5 ++ web/views/task_list_view.ex | 18 +++++ 13 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 priv/repo/migrations/20161209192504_create_task_list.exs create mode 100644 test/controllers/task_list_controller_test.exs create mode 100644 test/models/task_list_test.exs create mode 100644 web/controllers/task_list_controller.ex create mode 100644 web/models/task_list.ex create mode 100644 web/views/task_list_view.ex diff --git a/lib/code_corps/helpers/query.ex b/lib/code_corps/helpers/query.ex index 440e5a301..7b3743ff8 100644 --- a/lib/code_corps/helpers/query.ex +++ b/lib/code_corps/helpers/query.ex @@ -33,6 +33,12 @@ defmodule CodeCorps.Helpers.Query do end def project_filter(query, _), do: query + def task_list_filter(query, %{"task_list_ids" => task_list_ids}) do + task_list_ids = task_list_ids |> coalesce_id_string + query |> where([object], object.task_list_id in ^task_list_ids) + end + def task_list_filter(query, _), do: query + def task_type_filter(query, %{"task_type" => task_type_list}) do task_types = task_type_list |> coalesce_string query |> where([object], object.task_type in ^task_types) diff --git a/mix.exs b/mix.exs index 6b2cb28b0..e7b570118 100644 --- a/mix.exs +++ b/mix.exs @@ -90,7 +90,8 @@ defmodule CodeCorps.Mixfile do {:sentry, "~> 2.0"}, # Sentry error tracking {:stripity_stripe, "~> 2.0.0-alpha.5"}, # Stripe {:timex, "~> 3.0"}, - {:timex_ecto, "~> 3.0"} + {:timex_ecto, "~> 3.0"}, + {:ecto_ordered, "0.2.0-beta1"} ] end diff --git a/mix.lock b/mix.lock index b39cf1524..2b3e775eb 100644 --- a/mix.lock +++ b/mix.lock @@ -8,16 +8,17 @@ "canary": {:hex, :canary, "1.1.0", "3599012f5393c2fdb18c9129853a9fc6cd115ebfdcc0725af7b52398b9ed5c7b", [:mix], [{:canada, "~> 1.0.0", [hex: :canada, optional: false]}, {:ecto, ">= 1.1.0", [hex: :ecto, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]}, "certifi": {:hex, :certifi, "0.7.0", "861a57f3808f7eb0c2d1802afeaae0fa5de813b0df0979153cbafcd853ababaf", [:rebar3], []}, "combine": {:hex, :combine, "0.9.3", "192e609b48b3f2210494e26f85db1712657be1a8f15795656710317ea43fc449", [:mix], []}, - "comeonin": {:hex, :comeonin, "2.6.0", "74c288338b33205f9ce97e2117bb9a2aaab103a1811d243443d76fdb62f904ac", [:make, :make, :mix], []}, + "comeonin": {:hex, :comeonin, "2.6.0", "74c288338b33205f9ce97e2117bb9a2aaab103a1811d243443d76fdb62f904ac", [:mix, :make, :make], []}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, "corsica": {:hex, :corsica, "0.5.0", "eb5b2fccc5bc4f31b8e2b77dd15f5f302aca5d63286c953e8e916f806056d50c", [:mix], [{:cowboy, ">= 1.0.0", [hex: :cowboy, optional: false]}, {:plug, ">= 0.9.0", [hex: :plug, optional: false]}]}, - "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, + "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, "credo": {:hex, :credo, "0.5.2", "92e8c9f86e0ffbf9f688595e9f4e936bc96a52e5606d2c19713e9e4d191d5c74", [:mix], [{:bunt, "~> 0.1.6", [hex: :bunt, optional: false]}]}, "db_connection": {:hex, :db_connection, "1.1.0", "b2b88db6d7d12f99997b584d09fad98e560b817a20dab6a526830e339f54cdb3", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]}, "decimal": {:hex, :decimal, "1.3.1", "157b3cedb2bfcb5359372a7766dd7a41091ad34578296e951f58a946fcab49c6", [:mix], []}, "earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []}, "ecto": {:hex, :ecto, "2.0.6", "9dcbf819c2a77f67a66b83739b7fcc00b71aaf6c100016db4f798930fa4cfd47", [:mix], [{:db_connection, "~> 1.0", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.1.2 or ~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.12.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]}, + "ecto_ordered": {:hex, :ecto_ordered, "0.2.0-beta1", "cb066bc608f1c8913cea85af8293261720e6a88e3c99061e6877d7025352f045", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}]}, "ex_aws": {:hex, :ex_aws, "0.5.0", "6ca02f1e8fe8340aa2eee66d9f08efcd6ff1f9f4ef7264669d0756e0b3917218", [:mix], [{:httpoison, "~> 0.8", [hex: :httpoison, optional: true]}, {:jsx, "~> 2.5", [hex: :jsx, optional: true]}, {:poison, "~> 1.2 or ~> 2.0", [hex: :poison, optional: true]}, {:sweet_xml, "~> 0.5", [hex: :sweet_xml, optional: true]}]}, "ex_doc": {:hex, :ex_doc, "0.14.3", "e61cec6cf9731d7d23d254266ab06ac1decbb7651c3d1568402ec535d387b6f7", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}, "ex_machina": {:hex, :ex_machina, "1.0.2", "1cc49e1a09e3f7ab2ecb630c17f14c2872dc4ec145d6d05a9c3621936a63e34f", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: true]}]}, @@ -26,7 +27,7 @@ "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []}, "gettext": {:hex, :gettext, "0.12.1", "c0624f52763469ef7a3674919ae28b8286d88195b90fa1516180f31bbbd26d14", [:mix], []}, "guardian": {:hex, :guardian, "0.13.0", "37c5b5302617276093570ee938baca146f53e1d5de1f5c2b8effb1d2fea596d2", [:mix], [{:jose, "~> 1.8", [hex: :jose, optional: false]}, {:phoenix, "~> 1.2.0", [hex: :phoenix, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}, {:poison, ">= 1.3.0", [hex: :poison, optional: false]}, {:uuid, ">=1.1.1", [hex: :uuid, optional: false]}]}, - "hackney": {:hex, :hackney, "1.6.3", "d489d7ca2d4323e307bedc4bfe684323a7bf773ecfd77938f3ee8074e488e140", [:mix, :rebar3], [{:certifi, "0.7.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, + "hackney": {:hex, :hackney, "1.6.3", "d489d7ca2d4323e307bedc4bfe684323a7bf773ecfd77938f3ee8074e488e140", [:rebar3, :mix], [{:certifi, "0.7.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, "httpoison": {:hex, :httpoison, "0.10.0", "4727b3a5e57e9a4ff168a3c2883e20f1208103a41bccc4754f15a9366f49b676", [:mix], [{:hackney, "~> 1.6.3", [hex: :hackney, optional: false]}]}, "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, "inch_ex": {:hex, :inch_ex, "0.5.5", "b63f57e281467bd3456461525fdbc9e158c8edbe603da6e3e4671befde796a3d", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]}, @@ -53,7 +54,7 @@ "scrivener_ecto": {:hex, :scrivener_ecto, "1.0.2", "4b10a2e6c23ed8aae59731d7ae71bfd55afea6559aae61b124e6e521055b4a9c", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:postgrex, "~> 0.11.0 or ~> 0.12.0", [hex: :postgrex, optional: true]}, {:scrivener, "~> 2.0", [hex: :scrivener, optional: false]}]}, "segment": {:git, "https://github.com/stueccles/analytics-elixir.git", "8fe520c16a8a9290d55c849bf4d67420396e1cdd", []}, "sentry": {:hex, :sentry, "2.0.2", "f08638758f7bf891e238466009f6cd702fc26d87286663af26927a78ed149346", [:mix], [{:hackney, "~> 1.6.1", [hex: :hackney, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: true]}, {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}, {:uuid, "~> 1.0", [hex: :uuid, optional: false]}]}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:rebar, :make], []}, "stripe_eventex": {:hex, :stripe_eventex, "1.0.0", "782016598b751c0fdb5489038c92c30a5aab034636d0d9d3a486f75a01fbf0b6", [:mix], [{:cowboy, "~> 1.0.0", [hex: :cowboy, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}, {:poison, "~> 2.0", [hex: :poison, optional: false]}]}, "stripity_stripe": {:hex, :stripity_stripe, "2.0.0-alpha.5", "ba6d4ffc6251029135c76e9c6e2dd77580713f5c6833fb82da708336023bbfa2", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, optional: false]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]}, "timex": {:hex, :timex, "3.1.5", "413d6d8d6f0162a5d47080cb8ca520d790184ac43e097c95191c7563bf25b428", [:mix], [{:combine, "~> 0.7", [hex: :combine, optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, optional: false]}]}, diff --git a/priv/repo/migrations/20161209192504_create_task_list.exs b/priv/repo/migrations/20161209192504_create_task_list.exs new file mode 100644 index 000000000..e2c1925b6 --- /dev/null +++ b/priv/repo/migrations/20161209192504_create_task_list.exs @@ -0,0 +1,66 @@ +defmodule CodeCorps.Repo.Migrations.CreateTaskList do + use Ecto.Migration + import Ecto.Changeset + import Ecto.Query + + alias CodeCorps.Project + alias CodeCorps.Repo + alias CodeCorps.Task + alias CodeCorps.TaskList + + def change do + create table(:task_lists) do + add :name, :string + add :rank, :integer + add :project_id, references(:projects, on_delete: :nothing) + + timestamps() + end + + create index(:task_lists, [:project_id]) + + alter table(:tasks) do + add :task_list_id, references(:task_lists, on_delete: :nothing) + add :rank, :integer + end + + flush + + Application.ensure_all_started :timex + migrate_existing() + end + + def migrate_existing() do + Project + |> preload(:task_lists) + |> Repo.all() + |> Enum.each(&handle_project_migration/1) + end + + defp handle_project_migration(project) do + cond do + project.task_lists != [] -> + IO.puts "Task lists already exist for #{project.title}, skipping migration." + true -> + IO.puts "Generating default task lists for #{project.title}." + + {:ok, project} = Project.changeset(project, %{}) + |> put_assoc(:task_lists, TaskList.default_task_lists()) + |> Repo.update + + add_existing_tasks_to_inbox(project, hd(project.task_lists)) + end + end + + defp add_existing_tasks_to_inbox(project, task_list) do + Task + |> CodeCorps.Helpers.Query.project_filter(%{ project_id: project.id }) + |> Repo.all() + |> Enum.each(&assign_task_to_inbox(&1, task_list)) + end + + defp assign_task_to_inbox(task, task_list) do + Task.changeset(task, %{ task_list_id: task_list.id }) + |> Repo.update() + end +end diff --git a/test/controllers/task_list_controller_test.exs b/test/controllers/task_list_controller_test.exs new file mode 100644 index 000000000..036b5e919 --- /dev/null +++ b/test/controllers/task_list_controller_test.exs @@ -0,0 +1,62 @@ +defmodule CodeCorps.TaskListControllerTest do + use CodeCorps.ConnCase + + alias CodeCorps.TaskList + @valid_attrs %{name: "some content", position: 42} + @invalid_attrs %{} + + setup %{conn: conn} do + {:ok, conn: put_req_header(conn, "accept", "application/json")} + end + + test "lists all entries on index", %{conn: conn} do + conn = get conn, task_list_path(conn, :index) + assert json_response(conn, 200)["data"] == [] + end + + test "shows chosen resource", %{conn: conn} do + task_list = Repo.insert! %TaskList{} + conn = get conn, task_list_path(conn, :show, task_list) + assert json_response(conn, 200)["data"] == %{"id" => task_list.id, + "name" => task_list.name, + "position" => task_list.position, + "project_id" => task_list.project_id} + end + + test "renders page not found when id is nonexistent", %{conn: conn} do + assert_error_sent 404, fn -> + get conn, task_list_path(conn, :show, -1) + end + end + + test "creates and renders resource when data is valid", %{conn: conn} do + conn = post conn, task_list_path(conn, :create), task_list: @valid_attrs + assert json_response(conn, 201)["data"]["id"] + assert Repo.get_by(TaskList, @valid_attrs) + end + + test "does not create resource and renders errors when data is invalid", %{conn: conn} do + conn = post conn, task_list_path(conn, :create), task_list: @invalid_attrs + assert json_response(conn, 422)["errors"] != %{} + end + + test "updates and renders chosen resource when data is valid", %{conn: conn} do + task_list = Repo.insert! %TaskList{} + conn = put conn, task_list_path(conn, :update, task_list), task_list: @valid_attrs + assert json_response(conn, 200)["data"]["id"] + assert Repo.get_by(TaskList, @valid_attrs) + end + + test "does not update chosen resource and renders errors when data is invalid", %{conn: conn} do + task_list = Repo.insert! %TaskList{} + conn = put conn, task_list_path(conn, :update, task_list), task_list: @invalid_attrs + assert json_response(conn, 422)["errors"] != %{} + end + + test "deletes chosen resource", %{conn: conn} do + task_list = Repo.insert! %TaskList{} + conn = delete conn, task_list_path(conn, :delete, task_list) + assert response(conn, 204) + refute Repo.get(TaskList, task_list.id) + end +end diff --git a/test/models/task_list_test.exs b/test/models/task_list_test.exs new file mode 100644 index 000000000..24428cc78 --- /dev/null +++ b/test/models/task_list_test.exs @@ -0,0 +1,18 @@ +defmodule CodeCorps.TaskListTest do + use CodeCorps.ModelCase + + alias CodeCorps.TaskList + + @valid_attrs %{name: "some content", position: 42} + @invalid_attrs %{} + + test "changeset with valid attributes" do + changeset = TaskList.changeset(%TaskList{}, @valid_attrs) + assert changeset.valid? + end + + test "changeset with invalid attributes" do + changeset = TaskList.changeset(%TaskList{}, @invalid_attrs) + refute changeset.valid? + end +end diff --git a/web/controllers/task_controller.ex b/web/controllers/task_controller.ex index ee51b24a1..e1e4790d9 100644 --- a/web/controllers/task_controller.ex +++ b/web/controllers/task_controller.ex @@ -4,7 +4,7 @@ defmodule CodeCorps.TaskController do import CodeCorps.Helpers.Query, only: [ project_filter: 2, project_id_with_number_filter: 2, sort_by_newest_first: 1, - task_type_filter: 2, task_status_filter: 2 + task_list_filter: 2, task_type_filter: 2, task_status_filter: 2 ] alias CodeCorps.Task @@ -16,6 +16,7 @@ defmodule CodeCorps.TaskController do def handle_index(conn, params) do page = Task |> project_filter(params) + |> task_list_filter(params) |> task_type_filter(params) |> task_status_filter(params) |> sort_by_newest_first diff --git a/web/controllers/task_list_controller.ex b/web/controllers/task_list_controller.ex new file mode 100644 index 000000000..648f12ee0 --- /dev/null +++ b/web/controllers/task_list_controller.ex @@ -0,0 +1,55 @@ +defmodule CodeCorps.TaskListController do + use CodeCorps.Web, :controller + + alias CodeCorps.TaskList + + def index(conn, _params) do + task_lists = Repo.all(TaskList) + render(conn, "index.json", task_lists: task_lists) + end + + def create(conn, %{"task_list" => task_list_params}) do + changeset = TaskList.changeset(%TaskList{}, task_list_params) + + case Repo.insert(changeset) do + {:ok, task_list} -> + conn + |> put_status(:created) + |> put_resp_header("location", task_list_path(conn, :show, task_list)) + |> render("show.json", task_list: task_list) + {:error, changeset} -> + conn + |> put_status(:unprocessable_entity) + |> render(CodeCorps.ChangesetView, "error.json", changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + task_list = Repo.get!(TaskList, id) + render(conn, "show.json", task_list: task_list) + end + + def update(conn, %{"id" => id, "task_list" => task_list_params}) do + task_list = Repo.get!(TaskList, id) + changeset = TaskList.changeset(task_list, task_list_params) + + case Repo.update(changeset) do + {:ok, task_list} -> + render(conn, "show.json", task_list: task_list) + {:error, changeset} -> + conn + |> put_status(:unprocessable_entity) + |> render(CodeCorps.ChangesetView, "error.json", changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + task_list = Repo.get!(TaskList, id) + + # Here we use delete! (with a bang) because we expect + # it to always work (and if it does not, it will raise). + Repo.delete!(task_list) + + send_resp(conn, :no_content, "") + end +end diff --git a/web/models/project.ex b/web/models/project.ex index 21502ee00..75aa57d07 100644 --- a/web/models/project.ex +++ b/web/models/project.ex @@ -9,6 +9,7 @@ defmodule CodeCorps.Project do import CodeCorps.Helpers.Slug import CodeCorps.Validators.SlugValidator alias CodeCorps.Services.MarkdownRendererService + alias CodeCorps.TaskList schema "projects" do field :approved, :boolean @@ -28,6 +29,7 @@ defmodule CodeCorps.Project do has_many :donation_goals, CodeCorps.DonationGoal has_many :project_categories, CodeCorps.ProjectCategory has_many :project_skills, CodeCorps.ProjectSkill + has_many :task_lists, CodeCorps.TaskList has_many :tasks, CodeCorps.Task has_many :categories, through: [:project_categories, :category] @@ -55,7 +57,7 @@ defmodule CodeCorps.Project do struct |> cast(params, [:organization_id]) |> changeset(params) - + |> put_assoc(:task_lists, TaskList.default_task_lists()) end @doc """ diff --git a/web/models/task.ex b/web/models/task.ex index 464f1f462..4cb70880b 100644 --- a/web/models/task.ex +++ b/web/models/task.ex @@ -1,5 +1,6 @@ defmodule CodeCorps.Task do use CodeCorps.Web, :model + import EctoOrdered alias CodeCorps.Services.MarkdownRendererService schema "tasks" do @@ -10,9 +11,12 @@ defmodule CodeCorps.Task do field :state, :string field :status, :string, default: "open" field :title, :string + field :position, :integer, virtual: true + field :rank, :integer belongs_to :project, CodeCorps.Project belongs_to :user, CodeCorps.User + belongs_to :task_list, CodeCorps.TaskList has_many :comments, CodeCorps.Comment timestamps() @@ -20,9 +24,11 @@ defmodule CodeCorps.Task do def changeset(struct, params \\ %{}) do struct - |> cast(params, [:title, :markdown, :task_type]) + |> cast(params, [:title, :markdown, :task_type, :task_list_id]) |> validate_required([:title, :markdown, :task_type]) |> validate_inclusion(:task_type, task_types) + |> assoc_constraint(:task_list) + |> set_order(:position, :rank, :task_list_id) |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) end @@ -30,7 +36,7 @@ defmodule CodeCorps.Task do struct |> changeset(params) |> cast(params, [:project_id, :user_id]) - |> validate_required([:project_id, :user_id]) + |> validate_required([:project_id, :user_id, :task_list_id]) |> assoc_constraint(:project) |> assoc_constraint(:user) |> put_change(:state, "published") diff --git a/web/models/task_list.ex b/web/models/task_list.ex new file mode 100644 index 000000000..f2dd71153 --- /dev/null +++ b/web/models/task_list.ex @@ -0,0 +1,43 @@ +defmodule CodeCorps.TaskList do + use CodeCorps.Web, :model + import EctoOrdered + + schema "task_lists" do + field :name, :string + field :position, :integer, virtual: true + field :rank, :integer + + belongs_to :project, CodeCorps.Project + has_many :tasks, CodeCorps.Task + + timestamps() + end + + def default_task_lists() do + [ + %{ + name: "Inbox", + position: 1 + }, %{ + name: "Backlog", + position: 2 + }, %{ + name: "In Progress", + position: 3 + }, %{ + name: "Done", + position: 4 + } + ] + end + + @doc """ + Builds a changeset based on the `struct` and `params`. + """ + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:name, :position]) + |> validate_required([:name, :position]) + |> set_order(:position, :rank, :project_id) + end +end diff --git a/web/router.ex b/web/router.ex index af541b655..c73bac3e9 100644 --- a/web/router.ex +++ b/web/router.ex @@ -69,6 +69,7 @@ defmodule CodeCorps.Router do resources "/stripe-connect-subscriptions", StripeConnectSubscriptionController, only: [:show, :create] resources "/stripe-platform-cards", StripePlatformCardController, only: [:show, :create] resources "/stripe-platform-customers", StripePlatformCustomerController, only: [:show, :create] + resources "/task-lists", TaskListController, only: [:create, :update] resources "/tasks", TaskController, only: [:create, :update] resources "/users", UserController, only: [:update] resources "/user-categories", UserCategoryController, only: [:create, :delete] @@ -88,6 +89,7 @@ defmodule CodeCorps.Router do resources "/organizations", OrganizationController, only: [:index, :show] resources "/organization-memberships", OrganizationMembershipController, only: [:index, :show] resources "/projects", ProjectController, only: [:index, :show] do + resources "/task-lists", TaskListController, only: [:index, :show] resources "/tasks", TaskController, only: [:index, :show] end resources "/project-categories", ProjectCategoryController, only: [:index, :show] @@ -95,6 +97,9 @@ defmodule CodeCorps.Router do resources "/roles", RoleController, only: [:index, :show] resources "/role-skills", RoleSkillController, only: [:index, :show] resources "/skills", SkillController, only: [:index, :show] + resources "/task-lists", TaskListController, only: [:index, :show] do + resources "/tasks", TaskListController, only: [:index, :show] + end resources "/tasks", TaskController, only: [:index, :show] get "/users/email_available", UserController, :email_available get "/users/username_available", UserController, :username_available diff --git a/web/views/task_list_view.ex b/web/views/task_list_view.ex new file mode 100644 index 000000000..ff929aca8 --- /dev/null +++ b/web/views/task_list_view.ex @@ -0,0 +1,18 @@ +defmodule CodeCorps.TaskListView do + use CodeCorps.Web, :view + + def render("index.json", %{task_lists: task_lists}) do + %{data: render_many(task_lists, CodeCorps.TaskListView, "task_list.json")} + end + + def render("show.json", %{task_list: task_list}) do + %{data: render_one(task_list, CodeCorps.TaskListView, "task_list.json")} + end + + def render("task_list.json", %{task_list: task_list}) do + %{id: task_list.id, + name: task_list.name, + position: task_list.position, + project_id: task_list.project_id} + end +end From 77480ab6c71cf1a44841c0b56c48e1709e27d78f Mon Sep 17 00:00:00 2001 From: Andrew Walton Date: Mon, 12 Dec 2016 09:43:22 -0500 Subject: [PATCH 2/5] Fix seed data and task model tests. --- mix.lock | 1 + priv/repo/seeds.exs | 3 ++- test/models/task_test.exs | 16 +++++++++++----- test/support/factories.ex | 11 ++++++++++- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/mix.lock b/mix.lock index 2b3e775eb..11b928675 100644 --- a/mix.lock +++ b/mix.lock @@ -57,6 +57,7 @@ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:rebar, :make], []}, "stripe_eventex": {:hex, :stripe_eventex, "1.0.0", "782016598b751c0fdb5489038c92c30a5aab034636d0d9d3a486f75a01fbf0b6", [:mix], [{:cowboy, "~> 1.0.0", [hex: :cowboy, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}, {:poison, "~> 2.0", [hex: :poison, optional: false]}]}, "stripity_stripe": {:hex, :stripity_stripe, "2.0.0-alpha.5", "ba6d4ffc6251029135c76e9c6e2dd77580713f5c6833fb82da708336023bbfa2", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, optional: false]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]}, + "timber": {:hex, :timber, "0.4.7", "df3fcd79bcb4eb4b53874d906ef5f3a212937b4bc7b7c5b244745202cc389443", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: true]}, {:phoenix, "~> 1.2", [hex: :phoenix, optional: true]}, {:plug, "~> 1.2", [hex: :plug, optional: true]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]}, "timex": {:hex, :timex, "3.1.5", "413d6d8d6f0162a5d47080cb8ca520d790184ac43e097c95191c7563bf25b428", [:mix], [{:combine, "~> 0.7", [hex: :combine, optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, optional: false]}]}, "timex_ecto": {:hex, :timex_ecto, "3.0.5", "3ec6c25e10d2c0020958e5df64d2b5e690e441faa2c2259da8bc6bd3d7f39256", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:timex, "~> 3.0", [hex: :timex, optional: false]}]}, "tzdata": {:hex, :tzdata, "0.5.9", "575be217b039057a47e133b72838cbe104fb5329b19906ea4e66857001c37edb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, optional: false]}]}, diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index d2fac7313..bd2c39c5c 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -295,7 +295,8 @@ cond do status: "open", number: i, project_id: 1, - user_id: 1 + user_id: 1, + task_list_id: 1 }) |> Repo.insert! end diff --git a/test/models/task_test.exs b/test/models/task_test.exs index 844773949..39d20d5a5 100644 --- a/test/models/task_test.exs +++ b/test/models/task_test.exs @@ -42,12 +42,14 @@ defmodule CodeCorps.TaskTest do test "is valid with valid attributes" do user = insert(:user) project = insert(:project) + task_list = insert(:task_list) changeset = Task.create_changeset(%Task{}, %{ markdown: "some content", task_type: "issue", title: "some content", project_id: project.id, user_id: user.id, + task_list_id: task_list.id }) assert changeset.valid? end @@ -56,15 +58,18 @@ defmodule CodeCorps.TaskTest do user = insert(:user) project_a = insert(:project, title: "Project A") project_b = insert(:project, title: "Project B") + task_list_a = insert(:task_list, name: "Task List A", project: project_a) + task_list_b = insert(:task_list, name: "Task List B", project: project_b) - insert(:task, project: project_a, user: user, title: "Project A Task 1") - insert(:task, project: project_a, user: user, title: "Project A Task 2") + insert(:task, project: project_a, user: user, task_list: task_list_a, title: "Project A Task 1") + insert(:task, project: project_a, user: user, task_list: task_list_b, title: "Project A Task 2") - insert(:task, project: project_b, user: user, title: "Project B Task 1") + insert(:task, project: project_b, user: user, task_list: task_list_b, title: "Project B Task 1") changes = Map.merge(@valid_attrs, %{ project_id: project_a.id, - user_id: user.id + user_id: user.id, + task_list_id: task_list_a.id }) changeset = Task.create_changeset(%Task{}, changes) {:ok, result} = Repo.insert(changeset) @@ -72,7 +77,8 @@ defmodule CodeCorps.TaskTest do changes = Map.merge(@valid_attrs, %{ project_id: project_b.id, - user_id: user.id + user_id: user.id, + task_list_id: task_list_b.id }) changeset = Task.create_changeset(%Task{}, changes) {:ok, result} = Repo.insert(changeset) diff --git a/test/support/factories.ex b/test/support/factories.ex index 4a41d4168..b2c3a4fa4 100644 --- a/test/support/factories.ex +++ b/test/support/factories.ex @@ -52,7 +52,16 @@ defmodule CodeCorps.Factories do status: "open", state: "published", project: build(:project), - user: build(:user) + user: build(:user), + task_list: build(:task_list) + } + end + + def task_list_factory do + %CodeCorps.TaskList{ + name: "Test task list", + rank: 1000, + project: build(:project) } end From 7d45f0553851e90cfb6bd719cdad3227e0a6ea34 Mon Sep 17 00:00:00 2001 From: Andrew Walton Date: Mon, 12 Dec 2016 19:50:50 -0500 Subject: [PATCH 3/5] Added API and views for task lists Modified project API / view to return task lists Modified tasks API to order by rank instead of inserted date Modified tasks API / view to return task list Added task list API / view to get all / single task lists --- lib/code_corps/helpers/query.ex | 11 ++ test/controllers/project_controller_test.exs | 4 +- test/controllers/task_controller_test.exs | 11 +- .../controllers/task_list_controller_test.exs | 115 +++++++++++------- test/models/task_test.exs | 39 +++++- test/support/factories.ex | 2 +- test/views/project_view_test.exs | 9 +- test/views/task_list_view_test.exs | 66 ++++++++++ test/views/task_view_test.exs | 9 +- web/controllers/task_controller.ex | 14 ++- web/controllers/task_list_controller.ex | 63 +++------- web/models/task.ex | 10 +- web/router.ex | 3 +- web/views/project_view.ex | 3 +- web/views/task_list_view.ex | 17 +-- web/views/task_view.ex | 5 +- 16 files changed, 258 insertions(+), 123 deletions(-) create mode 100644 test/views/task_list_view_test.exs diff --git a/lib/code_corps/helpers/query.ex b/lib/code_corps/helpers/query.ex index 7b3743ff8..ee112306d 100644 --- a/lib/code_corps/helpers/query.ex +++ b/lib/code_corps/helpers/query.ex @@ -28,6 +28,12 @@ defmodule CodeCorps.Helpers.Query do end def project_id_with_number_filter(query, _), do: query + def task_list_id_with_number_filter(query, %{"id" => number, "task_list_id" => task_list_id}) do + IO.puts "YAY" + query |> where([object], object.number == ^number and object.task_list_id == ^task_list_id) + end + def task_list_id_with_number_filter(query, _), do: query + def project_filter(query, %{"project_id" => project_id}) do query |> where([object], object.project_id == ^project_id) end @@ -37,6 +43,9 @@ defmodule CodeCorps.Helpers.Query do task_list_ids = task_list_ids |> coalesce_id_string query |> where([object], object.task_list_id in ^task_list_ids) end + def task_list_filter(query, %{"task_list_id" => task_list_id}) do + query |> where([object], object.task_list_id == ^task_list_id) + end def task_list_filter(query, _), do: query def task_type_filter(query, %{"task_type" => task_type_list}) do @@ -64,6 +73,8 @@ defmodule CodeCorps.Helpers.Query do def sort_by_newest_first(query), do: query |> order_by([desc: :inserted_at]) + def sort_by_rank(query), do: query |> order_by([asc: :rank]) + # end sorting # finders diff --git a/test/controllers/project_controller_test.exs b/test/controllers/project_controller_test.exs index b87321332..3b29004df 100644 --- a/test/controllers/project_controller_test.exs +++ b/test/controllers/project_controller_test.exs @@ -74,7 +74,9 @@ defmodule CodeCorps.ProjectControllerTest do organization = insert(:organization) insert(:organization_membership, role: "admin", member: current_user, organization: organization) attrs = @valid_attrs |> Map.merge(%{organization: organization}) - assert conn |> request_create(attrs) |> json_response(201) + response = conn |> request_create(attrs) + assert %{assigns: %{data: %{task_lists: [_inbox, _backlog, _in_progress, _done]}}} = response + assert response |> json_response(201) end @tag authenticated: :admin diff --git a/test/controllers/task_controller_test.exs b/test/controllers/task_controller_test.exs index 47c65b894..9cf4ebed3 100644 --- a/test/controllers/task_controller_test.exs +++ b/test/controllers/task_controller_test.exs @@ -24,12 +24,12 @@ defmodule CodeCorps.TaskControllerTest do |> assert_ids_from_response([task_1.id, task_2.id]) end - test "lists all entries newest first", %{conn: conn} do + test "lists all entries, ordered by rank", %{conn: conn} do # Has to be done manually. Inserting as a list is too quick. # Field lacks the resolution to differentiate. - task_1 = insert(:task, inserted_at: Timex.to_date({2000, 1, 1})) - task_2 = insert(:task, inserted_at: Timex.to_date({2000, 1, 2})) - task_3 = insert(:task, inserted_at: Timex.to_date({2000, 1, 3})) + task_1 = insert(:task, rank: 3000) + task_2 = insert(:task, rank: 2000) + task_3 = insert(:task, rank: 1000) path = conn |> task_path(:index) json = conn |> get(path) |> json_response(200) @@ -138,7 +138,8 @@ defmodule CodeCorps.TaskControllerTest do @tag :authenticated test "creates and renders resource when data is valid", %{conn: conn, current_user: current_user} do project = insert(:project) - attrs = @valid_attrs |> Map.merge(%{project: project, user: current_user}) + task_list = insert(:task_list, project: project) + attrs = @valid_attrs |> Map.merge(%{project: project, user: current_user, task_list: task_list}) json = conn |> request_create(attrs) |> json_response(201) # ensure record is reloaded from database before serialized, since number is added diff --git a/test/controllers/task_list_controller_test.exs b/test/controllers/task_list_controller_test.exs index 036b5e919..e3937f09a 100644 --- a/test/controllers/task_list_controller_test.exs +++ b/test/controllers/task_list_controller_test.exs @@ -1,62 +1,83 @@ defmodule CodeCorps.TaskListControllerTest do - use CodeCorps.ConnCase + use CodeCorps.ApiCase, resource_name: :task_list - alias CodeCorps.TaskList - @valid_attrs %{name: "some content", position: 42} - @invalid_attrs %{} + @valid_attrs %{ + name: "Test task" + } - setup %{conn: conn} do - {:ok, conn: put_req_header(conn, "accept", "application/json")} - end + @invalid_attrs %{ + name: nil + } - test "lists all entries on index", %{conn: conn} do - conn = get conn, task_list_path(conn, :index) - assert json_response(conn, 200)["data"] == [] - end + describe "index" do + test "lists all entries", %{conn: conn} do + [task_list_1, task_list_2] = insert_pair(:task_list) - test "shows chosen resource", %{conn: conn} do - task_list = Repo.insert! %TaskList{} - conn = get conn, task_list_path(conn, :show, task_list) - assert json_response(conn, 200)["data"] == %{"id" => task_list.id, - "name" => task_list.name, - "position" => task_list.position, - "project_id" => task_list.project_id} - end + conn + |> request_index + |> json_response(200) + |> assert_ids_from_response([task_list_1.id, task_list_2.id]) + end + + test "lists all entries by rank", %{conn: conn} do + # Has to be done manually. Inserting as a list is too quick. + # Field lacks the resolution to differentiate. + project = insert(:project) + task_list_1 = insert(:task_list, project: project, rank: 2000) + task_list_2 = insert(:task_list, project: project, rank: 1000) + task_list_3 = insert(:task_list, project: project, rank: 3000) - test "renders page not found when id is nonexistent", %{conn: conn} do - assert_error_sent 404, fn -> - get conn, task_list_path(conn, :show, -1) + path = conn |> task_list_path(:index) + json = conn |> get(path) |> json_response(200) + + ids = + json["data"] + |> Enum.map(&Map.get(&1, "id")) + |> Enum.map(&Integer.parse/1) + |> Enum.map(fn({id, _rem}) -> id end) + + assert ids == [task_list_2.id, task_list_1.id, task_list_3.id] end - end - test "creates and renders resource when data is valid", %{conn: conn} do - conn = post conn, task_list_path(conn, :create), task_list: @valid_attrs - assert json_response(conn, 201)["data"]["id"] - assert Repo.get_by(TaskList, @valid_attrs) - end + test "lists all task lists for a project", %{conn: conn} do + project_1 = insert(:project) + project_2 = insert(:project) + insert(:task_list, project: project_1) + insert(:task_list, project: project_1) + insert(:task_list, project: project_2) - test "does not create resource and renders errors when data is invalid", %{conn: conn} do - conn = post conn, task_list_path(conn, :create), task_list: @invalid_attrs - assert json_response(conn, 422)["errors"] != %{} - end + json = + conn + |> get("projects/#{project_1.id}/task-lists") + |> json_response(200) - test "updates and renders chosen resource when data is valid", %{conn: conn} do - task_list = Repo.insert! %TaskList{} - conn = put conn, task_list_path(conn, :update, task_list), task_list: @valid_attrs - assert json_response(conn, 200)["data"]["id"] - assert Repo.get_by(TaskList, @valid_attrs) + assert json["data"] |> Enum.count == 2 + end end - test "does not update chosen resource and renders errors when data is invalid", %{conn: conn} do - task_list = Repo.insert! %TaskList{} - conn = put conn, task_list_path(conn, :update, task_list), task_list: @invalid_attrs - assert json_response(conn, 422)["errors"] != %{} - end + describe "show" do + test "shows chosen resource", %{conn: conn} do + task_list = insert(:task_list) + + conn + |> request_show(task_list) + |> json_response(200) + |> Map.get("data") + |> assert_result_id(task_list.id) + end + + test "shows task list by id for project", %{conn: conn} do + task_list = insert(:task_list) - test "deletes chosen resource", %{conn: conn} do - task_list = Repo.insert! %TaskList{} - conn = delete conn, task_list_path(conn, :delete, task_list) - assert response(conn, 204) - refute Repo.get(TaskList, task_list.id) + path = conn |> project_task_list_path(:show, task_list.project_id, task_list.id) + data = conn |> get(path) |> json_response(200) |> Map.get("data") + + assert data["id"] == "#{task_list.id}" + assert data["type"] == "task-list" + end + + test "renders 404 when id is nonexistent", %{conn: conn} do + assert conn |> request_show(:not_found) |> json_response(404) + end end end diff --git a/test/models/task_test.exs b/test/models/task_test.exs index 39d20d5a5..0b105b336 100644 --- a/test/models/task_test.exs +++ b/test/models/task_test.exs @@ -61,8 +61,8 @@ defmodule CodeCorps.TaskTest do task_list_a = insert(:task_list, name: "Task List A", project: project_a) task_list_b = insert(:task_list, name: "Task List B", project: project_b) - insert(:task, project: project_a, user: user, task_list: task_list_a, title: "Project A Task 1") - insert(:task, project: project_a, user: user, task_list: task_list_b, title: "Project A Task 2") + insert(:task, project: project_a, user: user, task_list: task_list_a, rank: 2000, title: "Project A Task 1") + insert(:task, project: project_a, user: user, task_list: task_list_a, rank: 1000, title: "Project A Task 2") insert(:task, project: project_b, user: user, task_list: task_list_b, title: "Project B Task 1") @@ -85,6 +85,41 @@ defmodule CodeCorps.TaskTest do assert result.number == 2 end + test "auto-assigns rank, beginning of list, scoped to task list" do + user = insert(:user) + project_a = insert(:project, title: "Project A") + task_list_a = insert(:task_list, name: "Task List A", project: project_a) + task_list_b = insert(:task_list, name: "Task List B", project: project_a) + + task_a_1 = insert(:task, project: project_a, user: user, task_list: task_list_a, rank: 2000, title: "Project A Task 1") + task_a_2 = insert(:task, project: project_a, user: user, task_list: task_list_a, rank: 1000, title: "Project A Task 2") + + task_b_1 = insert(:task, project: project_a, user: user, task_list: task_list_b, rank: 2000, title: "Project B Task 1") + task_b_2 = insert(:task, project: project_a, user: user, task_list: task_list_b, rank: 1000, title: "Project B Task 2") + + changes = Map.merge(@valid_attrs, %{ + project_id: project_a.id, + user_id: user.id, + task_list_id: task_list_a.id + }) + changeset = Task.create_changeset(%Task{}, changes) + {:ok, result_a} = Repo.insert(changeset) + assert result_a.rank < task_a_1.rank && result_a.rank < task_a_2.rank + + changes = Map.merge(@valid_attrs, %{ + project_id: project_a.id, + user_id: user.id, + task_list_id: task_list_b.id + }) + changeset = Task.create_changeset(%Task{}, changes) + {:ok, result_b} = Repo.insert(changeset) + assert result_b.rank < task_b_1.rank && result_b.rank < task_b_2.rank + + # Make sure that, given the same rank configuration between task lists, + # the auto-assigned rank is the same, meaning the ranking is correctly scoped + assert result_a.rank == result_b.rank + end + test "sets state to 'published'" do changeset = Task.create_changeset(%Task{}, %{}) assert changeset |> get_change(:state) == "published" diff --git a/test/support/factories.ex b/test/support/factories.ex index b2c3a4fa4..63c75b2dc 100644 --- a/test/support/factories.ex +++ b/test/support/factories.ex @@ -60,7 +60,7 @@ defmodule CodeCorps.Factories do def task_list_factory do %CodeCorps.TaskList{ name: "Test task list", - rank: 1000, + position: 1, project: build(:project) } end diff --git a/test/views/project_view_test.exs b/test/views/project_view_test.exs index 969175f66..dbde58792 100644 --- a/test/views/project_view_test.exs +++ b/test/views/project_view_test.exs @@ -11,7 +11,8 @@ defmodule CodeCorps.ProjectViewTest do project_category = insert(:project_category, project: project) project_skill = insert(:project_skill, project: project) stripe_connect_plan = insert(:stripe_connect_plan, project: project) - task = insert(:task, project: project) + task_list = insert(:task_list, project: project) + task = insert(:task, project: project, task_list: task_list) rendered_json = render(CodeCorps.ProjectView, "show.json-api", data: project) @@ -66,6 +67,12 @@ defmodule CodeCorps.ProjectViewTest do "type" => "stripe-connect-plan" } }, + "task-lists" => %{ + "data" => [%{ + "id" => task_list.id |> Integer.to_string, + "type" => "task-list" + }] + }, "tasks" => %{ "data" => [ %{ diff --git a/test/views/task_list_view_test.exs b/test/views/task_list_view_test.exs new file mode 100644 index 000000000..21bf45c44 --- /dev/null +++ b/test/views/task_list_view_test.exs @@ -0,0 +1,66 @@ +defmodule CodeCorps.TaskListViewTest do + use CodeCorps.ConnCase, async: true + + import Phoenix.View, only: [render: 3] + + test "renders all attributes and relationships properly" do + project = insert(:project) + task_list_a = insert(:task_list, rank: 1000, project: project) + task_list_b = insert(:task_list, rank: 500, project: project) + task = insert(:task, rank: 1000, task_list: task_list_a) + + rendered_json = render(CodeCorps.TaskListView, "show.json-api", data: [task_list_a, task_list_b]) + + expected_json = %{ + "data" => [%{ + "attributes" => %{ + "name" => task_list_a.name, + "rank" => 1000, + "inserted-at" => task_list_a.inserted_at, + "updated-at" => task_list_a.updated_at, + }, + "id" => task_list_a.id |> Integer.to_string, + "relationships" => %{ + "project" => %{ + "data" => %{ + "id" => task_list_a.project_id |> Integer.to_string, + "type" => "project" + } + }, + "tasks" => %{ + "data" => [%{ + "id" => task.id |> Integer.to_string, + "type" => "task" + }] + } + }, + "type" => "task-list", + }, %{ + "attributes" => %{ + "name" => task_list_b.name, + "rank" => 500, + "inserted-at" => task_list_b.inserted_at, + "updated-at" => task_list_b.updated_at, + }, + "id" => task_list_b.id |> Integer.to_string, + "relationships" => %{ + "project" => %{ + "data" => %{ + "id" => task_list_b.project_id |> Integer.to_string, + "type" => "project" + } + }, + "tasks" => %{ + "data" => [] + } + }, + "type" => "task-list" + }], + "jsonapi" => %{ + "version" => "1.0" + } + } + + assert rendered_json == expected_json + end +end diff --git a/test/views/task_view_test.exs b/test/views/task_view_test.exs index 0acc8ce88..cb04c031a 100644 --- a/test/views/task_view_test.exs +++ b/test/views/task_view_test.exs @@ -4,7 +4,7 @@ defmodule CodeCorps.TaskViewTest do import Phoenix.View, only: [render: 3] test "renders all attributes and relationships properly" do - task = insert(:task) + task = insert(:task, rank: 1000) comment = insert(:comment, task: task) rendered_json = render(CodeCorps.TaskView, "show.json-api", data: task) @@ -21,6 +21,7 @@ defmodule CodeCorps.TaskViewTest do "task-type" => task.task_type, "title" => task.title, "updated-at" => task.updated_at, + "rank" => task.rank }, "id" => task.id |> Integer.to_string, "relationships" => %{ @@ -43,6 +44,12 @@ defmodule CodeCorps.TaskViewTest do "id" => task.user_id |> Integer.to_string, "type" => "user" } + }, + "task-list" => %{ + "data" => %{ + "id" => task.task_list_id |> Integer.to_string, + "type" => "task-list" + } } }, "type" => "task", diff --git a/web/controllers/task_controller.ex b/web/controllers/task_controller.ex index e1e4790d9..dfb7f3a04 100644 --- a/web/controllers/task_controller.ex +++ b/web/controllers/task_controller.ex @@ -3,8 +3,8 @@ defmodule CodeCorps.TaskController do use JaResource import CodeCorps.Helpers.Query, only: [ - project_filter: 2, project_id_with_number_filter: 2, sort_by_newest_first: 1, - task_list_filter: 2, task_type_filter: 2, task_status_filter: 2 + project_filter: 2, project_id_with_number_filter: 2, task_list_id_with_number_filter: 2, + sort_by_rank: 1, task_list_filter: 2, task_type_filter: 2, task_status_filter: 2 ] alias CodeCorps.Task @@ -19,7 +19,7 @@ defmodule CodeCorps.TaskController do |> task_list_filter(params) |> task_type_filter(params) |> task_status_filter(params) - |> sort_by_newest_first + |> sort_by_rank |> Repo.paginate(params["page"] || %{}) # TODO: Once we are able to more easily add top-level meta @@ -45,6 +45,14 @@ defmodule CodeCorps.TaskController do |> project_id_with_number_filter(params) |> Repo.one end + + def record(%Plug.Conn{params: %{"task_list_id" => _task_list_id} = params}, _number_as_id) do + IO.inspect params + Task + |> task_list_id_with_number_filter(params) + |> Repo.one + end + def record(_conn, id), do: Task |> Repo.get(id) def handle_create(conn, attributes) do diff --git a/web/controllers/task_list_controller.ex b/web/controllers/task_list_controller.ex index 648f12ee0..8e6a07fd1 100644 --- a/web/controllers/task_list_controller.ex +++ b/web/controllers/task_list_controller.ex @@ -1,55 +1,30 @@ defmodule CodeCorps.TaskListController do use CodeCorps.Web, :controller + use JaResource - alias CodeCorps.TaskList - - def index(conn, _params) do - task_lists = Repo.all(TaskList) - render(conn, "index.json", task_lists: task_lists) - end + import CodeCorps.Helpers.Query, only: [ + project_filter: 2, sort_by_rank: 1, + ] - def create(conn, %{"task_list" => task_list_params}) do - changeset = TaskList.changeset(%TaskList{}, task_list_params) - - case Repo.insert(changeset) do - {:ok, task_list} -> - conn - |> put_status(:created) - |> put_resp_header("location", task_list_path(conn, :show, task_list)) - |> render("show.json", task_list: task_list) - {:error, changeset} -> - conn - |> put_status(:unprocessable_entity) - |> render(CodeCorps.ChangesetView, "error.json", changeset: changeset) - end - end + alias CodeCorps.TaskList - def show(conn, %{"id" => id}) do - task_list = Repo.get!(TaskList, id) - render(conn, "show.json", task_list: task_list) - end + plug :load_resource, model: TaskList, only: [:show] + plug JaResource - def update(conn, %{"id" => id, "task_list" => task_list_params}) do - task_list = Repo.get!(TaskList, id) - changeset = TaskList.changeset(task_list, task_list_params) + def handle_index(conn, params) do + tasks = TaskList + |> project_filter(params) + |> sort_by_rank + |> Repo.all() - case Repo.update(changeset) do - {:ok, task_list} -> - render(conn, "show.json", task_list: task_list) - {:error, changeset} -> - conn - |> put_status(:unprocessable_entity) - |> render(CodeCorps.ChangesetView, "error.json", changeset: changeset) - end + conn + |> render("index.json-api", data: tasks) end - def delete(conn, %{"id" => id}) do - task_list = Repo.get!(TaskList, id) - - # Here we use delete! (with a bang) because we expect - # it to always work (and if it does not, it will raise). - Repo.delete!(task_list) - - send_resp(conn, :no_content, "") + def record(%Plug.Conn{params: %{"project_id" => _project_id} = params}, id) do + TaskList + |> project_filter(params) + |> Repo.get(id) end + def record(_conn, id), do: TaskList |> Repo.get(id) end diff --git a/web/models/task.ex b/web/models/task.ex index 4cb70880b..1ca4bc4e2 100644 --- a/web/models/task.ex +++ b/web/models/task.ex @@ -24,10 +24,11 @@ defmodule CodeCorps.Task do def changeset(struct, params \\ %{}) do struct - |> cast(params, [:title, :markdown, :task_type, :task_list_id]) + |> cast(params, [:title, :markdown, :task_type, :task_list_id, :position]) |> validate_required([:title, :markdown, :task_type]) |> validate_inclusion(:task_type, task_types) |> assoc_constraint(:task_list) + |> apply_position() |> set_order(:position, :rank, :task_list_id) |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) end @@ -49,7 +50,14 @@ defmodule CodeCorps.Task do |> cast(params, [:status]) |> validate_inclusion(:status, statuses) |> put_change(:state, "edited") + end + def apply_position(changeset) do + case get_field(changeset, :position) do + nil -> + put_change(changeset, :position, 0) + _ -> changeset + end end defp task_types do diff --git a/web/router.ex b/web/router.ex index 81dbbfb8a..90b64dfb0 100644 --- a/web/router.ex +++ b/web/router.ex @@ -74,7 +74,6 @@ defmodule CodeCorps.Router do resources "/stripe-connect-subscriptions", StripeConnectSubscriptionController, only: [:show, :create] resources "/stripe-platform-cards", StripePlatformCardController, only: [:show, :create] resources "/stripe-platform-customers", StripePlatformCustomerController, only: [:show, :create] - resources "/task-lists", TaskListController, only: [:create, :update] resources "/tasks", TaskController, only: [:create, :update] resources "/users", UserController, only: [:update] resources "/user-categories", UserCategoryController, only: [:create, :delete] @@ -103,7 +102,7 @@ defmodule CodeCorps.Router do resources "/role-skills", RoleSkillController, only: [:index, :show] resources "/skills", SkillController, only: [:index, :show] resources "/task-lists", TaskListController, only: [:index, :show] do - resources "/tasks", TaskListController, only: [:index, :show] + resources "/tasks", TaskController, only: [:index, :show] end resources "/tasks", TaskController, only: [:index, :show] get "/users/email_available", UserController, :email_available diff --git a/web/views/project_view.ex b/web/views/project_view.ex index 1b8adbdc5..7b19fa3ee 100644 --- a/web/views/project_view.ex +++ b/web/views/project_view.ex @@ -2,7 +2,7 @@ defmodule CodeCorps.ProjectView do use CodeCorps.PreloadHelpers, default_preloads: [ :donation_goals, :organization, :project_categories, - :stripe_connect_plan, :project_skills, :tasks + :stripe_connect_plan, :project_skills, :task_lists, :tasks ] use CodeCorps.Web, :view use JaSerializer.PhoenixView @@ -18,6 +18,7 @@ defmodule CodeCorps.ProjectView do has_many :donation_goals, serializer: CodeCorps.DonationGoalView, identifiers: :always has_many :project_categories, serializer: CodeCorps.ProjectCategoryView, identifiers: :always has_many :project_skills, serializer: CodeCorps.ProjectSkillView, identifiers: :always + has_many :task_lists, serializer: CodeCorps.TaskListView, identifiers: :always has_many :tasks, serializer: CodeCorps.TaskView, identifiers: :always def donations_active(project, _conn) do diff --git a/web/views/task_list_view.ex b/web/views/task_list_view.ex index ff929aca8..d826173fb 100644 --- a/web/views/task_list_view.ex +++ b/web/views/task_list_view.ex @@ -1,18 +1,11 @@ defmodule CodeCorps.TaskListView do + use CodeCorps.PreloadHelpers, default_preloads: [:project, :tasks] use CodeCorps.Web, :view + use JaSerializer.PhoenixView - def render("index.json", %{task_lists: task_lists}) do - %{data: render_many(task_lists, CodeCorps.TaskListView, "task_list.json")} - end + attributes [:name, :rank, :inserted_at, :updated_at] - def render("show.json", %{task_list: task_list}) do - %{data: render_one(task_list, CodeCorps.TaskListView, "task_list.json")} - end + has_one :project, serializer: CodeCorps.ProjectView - def render("task_list.json", %{task_list: task_list}) do - %{id: task_list.id, - name: task_list.name, - position: task_list.position, - project_id: task_list.project_id} - end + has_many :tasks, serializer: CodeCorps.TaskView, identifiers: :always end diff --git a/web/views/task_view.ex b/web/views/task_view.ex index b7a610677..f3cf4924c 100644 --- a/web/views/task_view.ex +++ b/web/views/task_view.ex @@ -1,12 +1,13 @@ defmodule CodeCorps.TaskView do - use CodeCorps.PreloadHelpers, default_preloads: [:project, :user, :comments] + use CodeCorps.PreloadHelpers, default_preloads: [:project, :user, :task_list, :comments] use CodeCorps.Web, :view use JaSerializer.PhoenixView - attributes [:body, :markdown, :number, :task_type, :status, :state, :title, :inserted_at, :updated_at] + attributes [:body, :markdown, :number, :task_type, :status, :state, :title, :rank, :inserted_at, :updated_at] has_one :project, serializer: CodeCorps.ProjectView has_one :user, serializer: CodeCorps.UserView + has_one :task_list, serializer: CodeCorps.TaskListView has_many :comments, serializer: CodeCorps.CommentView, identifiers: :always end From 106f78ae7a930f96b6a7c90f46a6135fccdbd9f5 Mon Sep 17 00:00:00 2001 From: Andrew Walton Date: Tue, 13 Dec 2016 19:54:26 -0500 Subject: [PATCH 4/5] Address code review comments --- lib/code_corps/helpers/query.ex | 4 -- .../controllers/task_list_controller_test.exs | 12 ++---- test/test_helper.exs | 1 - test/views/project_view_test.exs | 10 +++-- test/views/task_list_view_test.exs | 41 +++++-------------- test/views/task_view_test.exs | 4 +- web/controllers/task_controller.ex | 1 - 7 files changed, 22 insertions(+), 51 deletions(-) diff --git a/lib/code_corps/helpers/query.ex b/lib/code_corps/helpers/query.ex index ee112306d..5aa5b74f5 100644 --- a/lib/code_corps/helpers/query.ex +++ b/lib/code_corps/helpers/query.ex @@ -29,7 +29,6 @@ defmodule CodeCorps.Helpers.Query do def project_id_with_number_filter(query, _), do: query def task_list_id_with_number_filter(query, %{"id" => number, "task_list_id" => task_list_id}) do - IO.puts "YAY" query |> where([object], object.number == ^number and object.task_list_id == ^task_list_id) end def task_list_id_with_number_filter(query, _), do: query @@ -43,9 +42,6 @@ defmodule CodeCorps.Helpers.Query do task_list_ids = task_list_ids |> coalesce_id_string query |> where([object], object.task_list_id in ^task_list_ids) end - def task_list_filter(query, %{"task_list_id" => task_list_id}) do - query |> where([object], object.task_list_id == ^task_list_id) - end def task_list_filter(query, _), do: query def task_type_filter(query, %{"task_type" => task_type_list}) do diff --git a/test/controllers/task_list_controller_test.exs b/test/controllers/task_list_controller_test.exs index e3937f09a..8e644d419 100644 --- a/test/controllers/task_list_controller_test.exs +++ b/test/controllers/task_list_controller_test.exs @@ -28,15 +28,11 @@ defmodule CodeCorps.TaskListControllerTest do task_list_3 = insert(:task_list, project: project, rank: 3000) path = conn |> task_list_path(:index) - json = conn |> get(path) |> json_response(200) - ids = - json["data"] - |> Enum.map(&Map.get(&1, "id")) - |> Enum.map(&Integer.parse/1) - |> Enum.map(fn({id, _rem}) -> id end) - - assert ids == [task_list_2.id, task_list_1.id, task_list_3.id] + conn + |> get(path) + |> json_response(200) + |> assert_ids_from_response([task_list_2.id, task_list_1.id, task_list_3.id]) end test "lists all task lists for a project", %{conn: conn} do diff --git a/test/test_helper.exs b/test/test_helper.exs index 966eb7ab1..4d6041097 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -8,4 +8,3 @@ ExUnit.configure exclude: [:requires_env] ExUnit.start Ecto.Adapters.SQL.Sandbox.mode(CodeCorps.Repo, :manual) - diff --git a/test/views/project_view_test.exs b/test/views/project_view_test.exs index dbde58792..bf8fdd28b 100644 --- a/test/views/project_view_test.exs +++ b/test/views/project_view_test.exs @@ -68,10 +68,12 @@ defmodule CodeCorps.ProjectViewTest do } }, "task-lists" => %{ - "data" => [%{ - "id" => task_list.id |> Integer.to_string, - "type" => "task-list" - }] + "data" => [ + %{ + "id" => task_list.id |> Integer.to_string, + "type" => "task-list" + } + ] }, "tasks" => %{ "data" => [ diff --git a/test/views/task_list_view_test.exs b/test/views/task_list_view_test.exs index 21bf45c44..cb1a8a7a9 100644 --- a/test/views/task_list_view_test.exs +++ b/test/views/task_list_view_test.exs @@ -5,25 +5,24 @@ defmodule CodeCorps.TaskListViewTest do test "renders all attributes and relationships properly" do project = insert(:project) - task_list_a = insert(:task_list, rank: 1000, project: project) - task_list_b = insert(:task_list, rank: 500, project: project) - task = insert(:task, rank: 1000, task_list: task_list_a) + task_list = insert(:task_list, rank: 1000, project: project) + task = insert(:task, rank: 1000, task_list: task_list) - rendered_json = render(CodeCorps.TaskListView, "show.json-api", data: [task_list_a, task_list_b]) + rendered_json = render(CodeCorps.TaskListView, "show.json-api", data: task_list) expected_json = %{ - "data" => [%{ + "data" => %{ "attributes" => %{ - "name" => task_list_a.name, + "name" => task_list.name, "rank" => 1000, - "inserted-at" => task_list_a.inserted_at, - "updated-at" => task_list_a.updated_at, + "inserted-at" => task_list.inserted_at, + "updated-at" => task_list.updated_at, }, - "id" => task_list_a.id |> Integer.to_string, + "id" => task_list.id |> Integer.to_string, "relationships" => %{ "project" => %{ "data" => %{ - "id" => task_list_a.project_id |> Integer.to_string, + "id" => task_list.project_id |> Integer.to_string, "type" => "project" } }, @@ -35,27 +34,7 @@ defmodule CodeCorps.TaskListViewTest do } }, "type" => "task-list", - }, %{ - "attributes" => %{ - "name" => task_list_b.name, - "rank" => 500, - "inserted-at" => task_list_b.inserted_at, - "updated-at" => task_list_b.updated_at, - }, - "id" => task_list_b.id |> Integer.to_string, - "relationships" => %{ - "project" => %{ - "data" => %{ - "id" => task_list_b.project_id |> Integer.to_string, - "type" => "project" - } - }, - "tasks" => %{ - "data" => [] - } - }, - "type" => "task-list" - }], + }, "jsonapi" => %{ "version" => "1.0" } diff --git a/test/views/task_view_test.exs b/test/views/task_view_test.exs index cb04c031a..35a1e8409 100644 --- a/test/views/task_view_test.exs +++ b/test/views/task_view_test.exs @@ -16,12 +16,12 @@ defmodule CodeCorps.TaskViewTest do "inserted-at" => task.inserted_at, "markdown" => task.markdown, "number" => task.number, + "rank" => task.rank, "status" => task.status, "state" => task.state, "task-type" => task.task_type, "title" => task.title, - "updated-at" => task.updated_at, - "rank" => task.rank + "updated-at" => task.updated_at }, "id" => task.id |> Integer.to_string, "relationships" => %{ diff --git a/web/controllers/task_controller.ex b/web/controllers/task_controller.ex index dfb7f3a04..ec0b17c2e 100644 --- a/web/controllers/task_controller.ex +++ b/web/controllers/task_controller.ex @@ -47,7 +47,6 @@ defmodule CodeCorps.TaskController do end def record(%Plug.Conn{params: %{"task_list_id" => _task_list_id} = params}, _number_as_id) do - IO.inspect params Task |> task_list_id_with_number_filter(params) |> Repo.one From 8ccbfafee921ac34f07d2c936e8d4c4bc5a49cab Mon Sep 17 00:00:00 2001 From: Andrew Walton Date: Tue, 13 Dec 2016 20:05:04 -0500 Subject: [PATCH 5/5] Change naming of 'rank' to 'order' --- lib/code_corps/helpers/query.ex | 2 +- .../20161209192504_create_task_list.exs | 4 ++-- test/controllers/task_controller_test.exs | 8 +++---- .../controllers/task_list_controller_test.exs | 8 +++---- test/models/task_test.exs | 24 +++++++++---------- test/views/task_list_view_test.exs | 6 ++--- test/views/task_view_test.exs | 4 ++-- web/controllers/task_controller.ex | 4 ++-- web/controllers/task_list_controller.ex | 4 ++-- web/models/task.ex | 4 ++-- web/models/task_list.ex | 4 ++-- web/views/task_list_view.ex | 2 +- web/views/task_view.ex | 2 +- 13 files changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/code_corps/helpers/query.ex b/lib/code_corps/helpers/query.ex index 5aa5b74f5..2c387e054 100644 --- a/lib/code_corps/helpers/query.ex +++ b/lib/code_corps/helpers/query.ex @@ -69,7 +69,7 @@ defmodule CodeCorps.Helpers.Query do def sort_by_newest_first(query), do: query |> order_by([desc: :inserted_at]) - def sort_by_rank(query), do: query |> order_by([asc: :rank]) + def sort_by_order(query), do: query |> order_by([asc: :order]) # end sorting diff --git a/priv/repo/migrations/20161209192504_create_task_list.exs b/priv/repo/migrations/20161209192504_create_task_list.exs index e2c1925b6..fa2d70fdf 100644 --- a/priv/repo/migrations/20161209192504_create_task_list.exs +++ b/priv/repo/migrations/20161209192504_create_task_list.exs @@ -11,7 +11,7 @@ defmodule CodeCorps.Repo.Migrations.CreateTaskList do def change do create table(:task_lists) do add :name, :string - add :rank, :integer + add :order, :integer add :project_id, references(:projects, on_delete: :nothing) timestamps() @@ -21,7 +21,7 @@ defmodule CodeCorps.Repo.Migrations.CreateTaskList do alter table(:tasks) do add :task_list_id, references(:task_lists, on_delete: :nothing) - add :rank, :integer + add :order, :integer end flush diff --git a/test/controllers/task_controller_test.exs b/test/controllers/task_controller_test.exs index 9cf4ebed3..b907228a4 100644 --- a/test/controllers/task_controller_test.exs +++ b/test/controllers/task_controller_test.exs @@ -24,12 +24,12 @@ defmodule CodeCorps.TaskControllerTest do |> assert_ids_from_response([task_1.id, task_2.id]) end - test "lists all entries, ordered by rank", %{conn: conn} do + test "lists all entries, ordered correctly", %{conn: conn} do # Has to be done manually. Inserting as a list is too quick. # Field lacks the resolution to differentiate. - task_1 = insert(:task, rank: 3000) - task_2 = insert(:task, rank: 2000) - task_3 = insert(:task, rank: 1000) + task_1 = insert(:task, order: 3000) + task_2 = insert(:task, order: 2000) + task_3 = insert(:task, order: 1000) path = conn |> task_path(:index) json = conn |> get(path) |> json_response(200) diff --git a/test/controllers/task_list_controller_test.exs b/test/controllers/task_list_controller_test.exs index 8e644d419..b0f6aca6c 100644 --- a/test/controllers/task_list_controller_test.exs +++ b/test/controllers/task_list_controller_test.exs @@ -19,13 +19,13 @@ defmodule CodeCorps.TaskListControllerTest do |> assert_ids_from_response([task_list_1.id, task_list_2.id]) end - test "lists all entries by rank", %{conn: conn} do + test "lists all entries by order", %{conn: conn} do # Has to be done manually. Inserting as a list is too quick. # Field lacks the resolution to differentiate. project = insert(:project) - task_list_1 = insert(:task_list, project: project, rank: 2000) - task_list_2 = insert(:task_list, project: project, rank: 1000) - task_list_3 = insert(:task_list, project: project, rank: 3000) + task_list_1 = insert(:task_list, project: project, order: 2000) + task_list_2 = insert(:task_list, project: project, order: 1000) + task_list_3 = insert(:task_list, project: project, order: 3000) path = conn |> task_list_path(:index) diff --git a/test/models/task_test.exs b/test/models/task_test.exs index 0b105b336..16a23e74c 100644 --- a/test/models/task_test.exs +++ b/test/models/task_test.exs @@ -61,8 +61,8 @@ defmodule CodeCorps.TaskTest do task_list_a = insert(:task_list, name: "Task List A", project: project_a) task_list_b = insert(:task_list, name: "Task List B", project: project_b) - insert(:task, project: project_a, user: user, task_list: task_list_a, rank: 2000, title: "Project A Task 1") - insert(:task, project: project_a, user: user, task_list: task_list_a, rank: 1000, title: "Project A Task 2") + insert(:task, project: project_a, user: user, task_list: task_list_a, order: 2000, title: "Project A Task 1") + insert(:task, project: project_a, user: user, task_list: task_list_a, order: 1000, title: "Project A Task 2") insert(:task, project: project_b, user: user, task_list: task_list_b, title: "Project B Task 1") @@ -85,17 +85,17 @@ defmodule CodeCorps.TaskTest do assert result.number == 2 end - test "auto-assigns rank, beginning of list, scoped to task list" do + test "auto-assigns order, beginning of list, scoped to task list" do user = insert(:user) project_a = insert(:project, title: "Project A") task_list_a = insert(:task_list, name: "Task List A", project: project_a) task_list_b = insert(:task_list, name: "Task List B", project: project_a) - task_a_1 = insert(:task, project: project_a, user: user, task_list: task_list_a, rank: 2000, title: "Project A Task 1") - task_a_2 = insert(:task, project: project_a, user: user, task_list: task_list_a, rank: 1000, title: "Project A Task 2") + task_a_1 = insert(:task, project: project_a, user: user, task_list: task_list_a, order: 2000, title: "Project A Task 1") + task_a_2 = insert(:task, project: project_a, user: user, task_list: task_list_a, order: 1000, title: "Project A Task 2") - task_b_1 = insert(:task, project: project_a, user: user, task_list: task_list_b, rank: 2000, title: "Project B Task 1") - task_b_2 = insert(:task, project: project_a, user: user, task_list: task_list_b, rank: 1000, title: "Project B Task 2") + task_b_1 = insert(:task, project: project_a, user: user, task_list: task_list_b, order: 2000, title: "Project B Task 1") + task_b_2 = insert(:task, project: project_a, user: user, task_list: task_list_b, order: 1000, title: "Project B Task 2") changes = Map.merge(@valid_attrs, %{ project_id: project_a.id, @@ -104,7 +104,7 @@ defmodule CodeCorps.TaskTest do }) changeset = Task.create_changeset(%Task{}, changes) {:ok, result_a} = Repo.insert(changeset) - assert result_a.rank < task_a_1.rank && result_a.rank < task_a_2.rank + assert result_a.order < task_a_1.order && result_a.order < task_a_2.order changes = Map.merge(@valid_attrs, %{ project_id: project_a.id, @@ -113,11 +113,11 @@ defmodule CodeCorps.TaskTest do }) changeset = Task.create_changeset(%Task{}, changes) {:ok, result_b} = Repo.insert(changeset) - assert result_b.rank < task_b_1.rank && result_b.rank < task_b_2.rank + assert result_b.order < task_b_1.order && result_b.order < task_b_2.order - # Make sure that, given the same rank configuration between task lists, - # the auto-assigned rank is the same, meaning the ranking is correctly scoped - assert result_a.rank == result_b.rank + # Make sure that, given the same order configuration between task lists, + # the auto-assigned order is the same, meaning the order is correctly scoped + assert result_a.order == result_b.order end test "sets state to 'published'" do diff --git a/test/views/task_list_view_test.exs b/test/views/task_list_view_test.exs index cb1a8a7a9..fe828888c 100644 --- a/test/views/task_list_view_test.exs +++ b/test/views/task_list_view_test.exs @@ -5,8 +5,8 @@ defmodule CodeCorps.TaskListViewTest do test "renders all attributes and relationships properly" do project = insert(:project) - task_list = insert(:task_list, rank: 1000, project: project) - task = insert(:task, rank: 1000, task_list: task_list) + task_list = insert(:task_list, order: 1000, project: project) + task = insert(:task, order: 1000, task_list: task_list) rendered_json = render(CodeCorps.TaskListView, "show.json-api", data: task_list) @@ -14,7 +14,7 @@ defmodule CodeCorps.TaskListViewTest do "data" => %{ "attributes" => %{ "name" => task_list.name, - "rank" => 1000, + "order" => 1000, "inserted-at" => task_list.inserted_at, "updated-at" => task_list.updated_at, }, diff --git a/test/views/task_view_test.exs b/test/views/task_view_test.exs index 35a1e8409..e677ae9e9 100644 --- a/test/views/task_view_test.exs +++ b/test/views/task_view_test.exs @@ -4,7 +4,7 @@ defmodule CodeCorps.TaskViewTest do import Phoenix.View, only: [render: 3] test "renders all attributes and relationships properly" do - task = insert(:task, rank: 1000) + task = insert(:task, order: 1000) comment = insert(:comment, task: task) rendered_json = render(CodeCorps.TaskView, "show.json-api", data: task) @@ -16,7 +16,7 @@ defmodule CodeCorps.TaskViewTest do "inserted-at" => task.inserted_at, "markdown" => task.markdown, "number" => task.number, - "rank" => task.rank, + "order" => task.order, "status" => task.status, "state" => task.state, "task-type" => task.task_type, diff --git a/web/controllers/task_controller.ex b/web/controllers/task_controller.ex index ec0b17c2e..b61f969cf 100644 --- a/web/controllers/task_controller.ex +++ b/web/controllers/task_controller.ex @@ -4,7 +4,7 @@ defmodule CodeCorps.TaskController do import CodeCorps.Helpers.Query, only: [ project_filter: 2, project_id_with_number_filter: 2, task_list_id_with_number_filter: 2, - sort_by_rank: 1, task_list_filter: 2, task_type_filter: 2, task_status_filter: 2 + sort_by_order: 1, task_list_filter: 2, task_type_filter: 2, task_status_filter: 2 ] alias CodeCorps.Task @@ -19,7 +19,7 @@ defmodule CodeCorps.TaskController do |> task_list_filter(params) |> task_type_filter(params) |> task_status_filter(params) - |> sort_by_rank + |> sort_by_order |> Repo.paginate(params["page"] || %{}) # TODO: Once we are able to more easily add top-level meta diff --git a/web/controllers/task_list_controller.ex b/web/controllers/task_list_controller.ex index 8e6a07fd1..ee9b17ce8 100644 --- a/web/controllers/task_list_controller.ex +++ b/web/controllers/task_list_controller.ex @@ -3,7 +3,7 @@ defmodule CodeCorps.TaskListController do use JaResource import CodeCorps.Helpers.Query, only: [ - project_filter: 2, sort_by_rank: 1, + project_filter: 2, sort_by_order: 1, ] alias CodeCorps.TaskList @@ -14,7 +14,7 @@ defmodule CodeCorps.TaskListController do def handle_index(conn, params) do tasks = TaskList |> project_filter(params) - |> sort_by_rank + |> sort_by_order |> Repo.all() conn diff --git a/web/models/task.ex b/web/models/task.ex index 1ca4bc4e2..e183781f7 100644 --- a/web/models/task.ex +++ b/web/models/task.ex @@ -12,7 +12,7 @@ defmodule CodeCorps.Task do field :status, :string, default: "open" field :title, :string field :position, :integer, virtual: true - field :rank, :integer + field :order, :integer belongs_to :project, CodeCorps.Project belongs_to :user, CodeCorps.User @@ -29,7 +29,7 @@ defmodule CodeCorps.Task do |> validate_inclusion(:task_type, task_types) |> assoc_constraint(:task_list) |> apply_position() - |> set_order(:position, :rank, :task_list_id) + |> set_order(:position, :order, :task_list_id) |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) end diff --git a/web/models/task_list.ex b/web/models/task_list.ex index f2dd71153..4a1abd48e 100644 --- a/web/models/task_list.ex +++ b/web/models/task_list.ex @@ -5,7 +5,7 @@ defmodule CodeCorps.TaskList do schema "task_lists" do field :name, :string field :position, :integer, virtual: true - field :rank, :integer + field :order, :integer belongs_to :project, CodeCorps.Project has_many :tasks, CodeCorps.Task @@ -38,6 +38,6 @@ defmodule CodeCorps.TaskList do struct |> cast(params, [:name, :position]) |> validate_required([:name, :position]) - |> set_order(:position, :rank, :project_id) + |> set_order(:position, :order, :project_id) end end diff --git a/web/views/task_list_view.ex b/web/views/task_list_view.ex index d826173fb..4dddcf0c2 100644 --- a/web/views/task_list_view.ex +++ b/web/views/task_list_view.ex @@ -3,7 +3,7 @@ defmodule CodeCorps.TaskListView do use CodeCorps.Web, :view use JaSerializer.PhoenixView - attributes [:name, :rank, :inserted_at, :updated_at] + attributes [:name, :order, :inserted_at, :updated_at] has_one :project, serializer: CodeCorps.ProjectView diff --git a/web/views/task_view.ex b/web/views/task_view.ex index f3cf4924c..6321919b9 100644 --- a/web/views/task_view.ex +++ b/web/views/task_view.ex @@ -3,7 +3,7 @@ defmodule CodeCorps.TaskView do use CodeCorps.Web, :view use JaSerializer.PhoenixView - attributes [:body, :markdown, :number, :task_type, :status, :state, :title, :rank, :inserted_at, :updated_at] + attributes [:body, :markdown, :number, :task_type, :status, :state, :title, :order, :inserted_at, :updated_at] has_one :project, serializer: CodeCorps.ProjectView has_one :user, serializer: CodeCorps.UserView