diff --git a/lib/code_corps/model/task_list.ex b/lib/code_corps/model/task_list.ex index 394d92979..f3a2111fd 100644 --- a/lib/code_corps/model/task_list.ex +++ b/lib/code_corps/model/task_list.ex @@ -7,10 +7,12 @@ defmodule CodeCorps.TaskList do @type t :: %__MODULE__{} schema "task_lists" do + field :done, :boolean, default: false field :inbox, :boolean, default: false field :name, :string field :order, :integer field :position, :integer, virtual: true + field :pull_requests, :boolean, default: false belongs_to :project, CodeCorps.Project has_many :tasks, CodeCorps.Task @@ -29,11 +31,11 @@ defmodule CodeCorps.TaskList do name: "Backlog", position: 2 }, %{ - inbox: false, + pull_requests: true, name: "In Progress", position: 3 }, %{ - inbox: false, + done: true, name: "Done", position: 4 } @@ -52,8 +54,11 @@ defmodule CodeCorps.TaskList do def create_changeset(struct, params) do struct - |> cast(params, [:inbox]) + |> cast(params, [:done, :inbox, :pull_requests]) |> changeset(params) - |> validate_required([:inbox]) + |> validate_required([:done, :inbox, :pull_requests]) + |> unique_constraint(:done, name: "task_lists_project_id_done_index") + |> unique_constraint(:inbox, name: "task_lists_project_id_inbox_index") + |> unique_constraint(:pull_requests, name: "task_lists_project_id_pull_requests_index") end end diff --git a/lib/code_corps_web/views/task_list_view.ex b/lib/code_corps_web/views/task_list_view.ex index 63bb18cb6..a6ccad2e4 100644 --- a/lib/code_corps_web/views/task_list_view.ex +++ b/lib/code_corps_web/views/task_list_view.ex @@ -3,7 +3,7 @@ defmodule CodeCorpsWeb.TaskListView do use CodeCorpsWeb, :view use JaSerializer.PhoenixView - attributes [:inbox, :name, :order, :inserted_at, :updated_at] + attributes [:done, :inbox, :name, :order, :pull_requests, :inserted_at, :updated_at] has_one :project, type: "project", field: :project_id diff --git a/priv/repo/migrations/20171106045740_add_done_to_task_list.exs b/priv/repo/migrations/20171106045740_add_done_to_task_list.exs new file mode 100644 index 000000000..19c904564 --- /dev/null +++ b/priv/repo/migrations/20171106045740_add_done_to_task_list.exs @@ -0,0 +1,37 @@ +defmodule CodeCorps.Repo.Migrations.AddDoneToTaskList do + use Ecto.Migration + + import Ecto.Query + + alias CodeCorps.Repo + + def up do + alter table(:task_lists) do + add :done, :boolean, default: false + end + + flush() + + from(tl in "task_lists", where: [name: "Done"], update: [set: [done: true]]) + |> Repo.update_all([]) + + task_list_query = + from(tl in "task_lists", where: [done: true], select: [:id]) + + # tests do not have any data, so we need to account for potential nil + case task_list_query |> Repo.one do + %{id: done_list_id} -> + task_update_query = from t in "tasks", + where: [status: "closed"], + update: [set: [task_list_id: ^done_list_id]] + task_update_query |> Repo.update_all([]) + nil -> nil + end + end + + def down do + alter table(:task_lists) do + remove :done + end + end +end diff --git a/priv/repo/migrations/20171106050209_add_pull_requests_to_task_list.exs b/priv/repo/migrations/20171106050209_add_pull_requests_to_task_list.exs new file mode 100644 index 000000000..d9a6cd527 --- /dev/null +++ b/priv/repo/migrations/20171106050209_add_pull_requests_to_task_list.exs @@ -0,0 +1,54 @@ +defmodule CodeCorps.Repo.Migrations.AddPullRequestsToTaskList do + use Ecto.Migration + + import Ecto.Query + + alias CodeCorps.Repo + + def up do + alter table(:task_lists) do + add :pull_requests, :boolean, default: false + end + + flush() + + # set all "In Progress" task lists to now contain pull requests + from( + tl in "task_lists", + where: [name: "In Progress"], + update: [set: [pull_requests: true]] + ) |> Repo.update_all([]) + + # get projects paired with associated pull request task list as ids + task_parent_data = from( + p in "projects", + left_join: + tl in "task_lists", + on: tl.project_id == p.id, + where: tl.pull_requests == true, + select: {p.id, tl.id} + ) |> Repo.all + + # get all tasks for projects, associated to github pull requests and + # assign them to the pull request task list + task_parent_data |> Enum.each(fn {project_id, pr_list_id} -> + from( + t in "tasks", + where: [project_id: ^project_id], + where: t.status != "closed", + where: not is_nil(t.github_issue_id), + inner_join: + gi in "github_issues", + on: t.github_issue_id == gi.id, + where: not is_nil(gi.github_pull_request_id), + update: [set: [task_list_id: ^pr_list_id]] + ) |> Repo.update_all([]) + end) + end + + def down do + alter table(:task_lists) do + remove :pull_requests + end + end +end diff --git a/priv/repo/migrations/20171106103153_add_unique_constraints_to_specific_task_lists.exs b/priv/repo/migrations/20171106103153_add_unique_constraints_to_specific_task_lists.exs new file mode 100644 index 000000000..1b06e7051 --- /dev/null +++ b/priv/repo/migrations/20171106103153_add_unique_constraints_to_specific_task_lists.exs @@ -0,0 +1,21 @@ +defmodule CodeCorps.Repo.Migrations.AddUniqueConstraintsToSpecificTaskLists do + @moduledoc false + + use Ecto.Migration + + def change do + # There is already a "task_lists_project_id_index", so we name explicitly + + create unique_index( + "task_lists", [:project_id], + where: "done = true", name: "task_lists_project_id_done_index") + + create unique_index( + "task_lists", [:project_id], + where: "pull_requests = true", name: "task_lists_project_id_pull_requests_index") + + create unique_index( + "task_lists", [:project_id], + where: "inbox = true", name: "task_lists_project_id_inbox_index") + end +end diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index b9f936321..b1188b6e4 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -1486,7 +1486,9 @@ CREATE TABLE task_lists ( project_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, - inbox boolean DEFAULT false + inbox boolean DEFAULT false, + done boolean DEFAULT false, + pull_requests boolean DEFAULT false ); @@ -3072,6 +3074,20 @@ CREATE INDEX task_lists_inbox_index ON task_lists USING btree (inbox); CREATE INDEX task_lists_order_index ON task_lists USING btree ("order"); +-- +-- Name: task_lists_project_id_done_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX task_lists_project_id_done_index ON task_lists USING btree (project_id) WHERE (done = true); + + +-- +-- Name: task_lists_project_id_inbox_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX task_lists_project_id_inbox_index ON task_lists USING btree (project_id) WHERE (inbox = true); + + -- -- Name: task_lists_project_id_index; Type: INDEX; Schema: public; Owner: - -- @@ -3079,6 +3095,13 @@ CREATE INDEX task_lists_order_index ON task_lists USING btree ("order"); CREATE INDEX task_lists_project_id_index ON task_lists USING btree (project_id); +-- +-- Name: task_lists_project_id_pull_requests_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX task_lists_project_id_pull_requests_index ON task_lists USING btree (project_id) WHERE (pull_requests = true); + + -- -- Name: task_skills_skill_id_index; Type: INDEX; Schema: public; Owner: - -- @@ -3844,5 +3867,5 @@ ALTER TABLE ONLY users -- PostgreSQL database dump complete -- -INSERT INTO "schema_migrations" (version) VALUES (20160723215749), (20160804000000), (20160804001111), (20160805132301), (20160805203929), (20160808143454), (20160809214736), (20160810124357), (20160815125009), (20160815143002), (20160816020347), (20160816034021), (20160817220118), (20160818000944), (20160818132546), (20160820113856), (20160820164905), (20160822002438), (20160822004056), (20160822011624), (20160822020401), (20160822044612), (20160830081224), (20160830224802), (20160911233738), (20160912002705), (20160912145957), (20160918003206), (20160928232404), (20161003185918), (20161019090945), (20161019110737), (20161020144622), (20161021131026), (20161031001615), (20161121005339), (20161121014050), (20161121043941), (20161121045709), (20161122015942), (20161123081114), (20161123150943), (20161124085742), (20161125200620), (20161126045705), (20161127054559), (20161205024856), (20161207112519), (20161209192504), (20161212005641), (20161214005935), (20161215052051), (20161216051447), (20161218005913), (20161219160401), (20161219163909), (20161220141753), (20161221085759), (20161226213600), (20161231063614), (20170102130055), (20170102181053), (20170104113708), (20170104212623), (20170104235423), (20170106013143), (20170115035159), (20170115230549), (20170121014100), (20170131234029), (20170201014901), (20170201025454), (20170201035458), (20170201183258), (20170220032224), (20170224233516), (20170226050552), (20170228085250), (20170308214128), (20170308220713), (20170308222552), (20170313130611), (20170318032449), (20170318082740), (20170324194827), (20170424215355), (20170501225441), (20170505224222), (20170526095401), (20170602000208), (20170622205732), (20170626231059), (20170628092119), (20170628213609), (20170629183404), (20170630140136), (20170706132431), (20170707213648), (20170711122252), (20170717092127), (20170725060612), (20170727052644), (20170731130121), (20170814131722), (20170913114958), (20170921014405), (20170925214512), (20170925230419), (20170926134646), (20170927100300), (20170928234412), (20171003134956), (20171003225853), (20171006063358), (20171006161407), (20171012215106), (20171012221231), (20171016125229), (20171016125516), (20171016223356), (20171016235656), (20171017235433), (20171019191035), (20171025184225), (20171026010933), (20171027061833), (20171028011642), (20171028173508), (20171030182857), (20171031232023), (20171031234356), (20171101023309), (20171104013543); +INSERT INTO "schema_migrations" (version) VALUES (20160723215749), (20160804000000), (20160804001111), (20160805132301), (20160805203929), (20160808143454), (20160809214736), (20160810124357), (20160815125009), (20160815143002), (20160816020347), (20160816034021), (20160817220118), (20160818000944), (20160818132546), (20160820113856), (20160820164905), (20160822002438), (20160822004056), (20160822011624), (20160822020401), (20160822044612), (20160830081224), (20160830224802), (20160911233738), (20160912002705), (20160912145957), (20160918003206), (20160928232404), (20161003185918), (20161019090945), (20161019110737), (20161020144622), (20161021131026), (20161031001615), (20161121005339), (20161121014050), (20161121043941), (20161121045709), (20161122015942), (20161123081114), (20161123150943), (20161124085742), (20161125200620), (20161126045705), (20161127054559), (20161205024856), (20161207112519), (20161209192504), (20161212005641), (20161214005935), (20161215052051), (20161216051447), (20161218005913), (20161219160401), (20161219163909), (20161220141753), (20161221085759), (20161226213600), (20161231063614), (20170102130055), (20170102181053), (20170104113708), (20170104212623), (20170104235423), (20170106013143), (20170115035159), (20170115230549), (20170121014100), (20170131234029), (20170201014901), (20170201025454), (20170201035458), (20170201183258), (20170220032224), (20170224233516), (20170226050552), (20170228085250), (20170308214128), (20170308220713), (20170308222552), (20170313130611), (20170318032449), (20170318082740), (20170324194827), (20170424215355), (20170501225441), (20170505224222), (20170526095401), (20170602000208), (20170622205732), (20170626231059), (20170628092119), (20170628213609), (20170629183404), (20170630140136), (20170706132431), (20170707213648), (20170711122252), (20170717092127), (20170725060612), (20170727052644), (20170731130121), (20170814131722), (20170913114958), (20170921014405), (20170925214512), (20170925230419), (20170926134646), (20170927100300), (20170928234412), (20171003134956), (20171003225853), (20171006063358), (20171006161407), (20171012215106), (20171012221231), (20171016125229), (20171016125516), (20171016223356), (20171016235656), (20171017235433), (20171019191035), (20171025184225), (20171026010933), (20171027061833), (20171028011642), (20171028173508), (20171030182857), (20171031232023), (20171031234356), (20171101023309), (20171104013543), (20171106045740), (20171106050209), (20171106103153); diff --git a/test/lib/code_corps/model/task_list_test.exs b/test/lib/code_corps/model/task_list_test.exs index f3c7d663b..33f9a9349 100644 --- a/test/lib/code_corps/model/task_list_test.exs +++ b/test/lib/code_corps/model/task_list_test.exs @@ -2,6 +2,7 @@ defmodule CodeCorps.TaskListTest do use CodeCorps.ModelCase alias CodeCorps.TaskList + alias Ecto.Changeset @valid_attrs %{name: "some content", position: 42} @invalid_attrs %{} @@ -16,12 +17,122 @@ defmodule CodeCorps.TaskListTest do refute changeset.valid? end - test "is not inbox by default" do + test "defaults :done to 'false'" do {:ok, record} = - %TaskList{} - |> TaskList.changeset(@valid_attrs) - |> CodeCorps.Repo.insert + %TaskList{} |> TaskList.changeset(@valid_attrs) |> Repo.insert + assert record.done == false + end + + test "defaults :inbox to 'false'" do + {:ok, record} = + %TaskList{} |> TaskList.changeset(@valid_attrs) |> Repo.insert + assert record.inbox == false + end + + test "defaults :pull_requests to 'false'" do + {:ok, record} = + %TaskList{} |> TaskList.changeset(@valid_attrs) |> Repo.insert + assert record.pull_requests == false + end + + describe "create_changeset" do + test "casts done" do + attrs = @valid_attrs |> Map.merge(%{done: true}) + changeset = %TaskList{} |> TaskList.create_changeset(attrs) + assert changeset |> Changeset.get_change(:done) == true + end + + test "casts inbox" do + attrs = @valid_attrs |> Map.merge(%{inbox: true}) + changeset = %TaskList{} |> TaskList.create_changeset(attrs) + assert changeset |> Changeset.get_change(:inbox) == true + end + + test "casts pull_requests" do + attrs = @valid_attrs |> Map.merge(%{pull_requests: true}) + changeset = %TaskList{} |> TaskList.create_changeset(attrs) + assert changeset |> Changeset.get_change(:pull_requests) == true + end + + test "requires done" do + attrs = @valid_attrs |> Map.merge(%{done: nil}) + changeset = %TaskList{} |> TaskList.create_changeset(attrs) + refute changeset.valid? + assert changeset |> Map.get(:errors) |> Keyword.get(:done) + end + + test "requires inbox" do + attrs = @valid_attrs |> Map.merge(%{inbox: nil}) + changeset = %TaskList{} |> TaskList.create_changeset(attrs) + refute changeset.valid? + assert changeset |> Map.get(:errors) |> Keyword.get(:inbox) + end + + test "requires pull_requests" do + attrs = @valid_attrs |> Map.merge(%{pull_requests: nil}) + changeset = %TaskList{} |> TaskList.create_changeset(attrs) + refute changeset.valid? + assert changeset |> Map.get(:errors) |> Keyword.get(:pull_requests) + end + + test "ensures a unique 'done' task list per project" do + %{id: project_id} = insert(:project) + attrs = @valid_attrs |> Map.merge(%{done: true}) + + {:ok, _task_list} = + %TaskList{} + |> TaskList.create_changeset(attrs) + |> Changeset.put_change(:project_id, project_id) + |> Repo.insert + + {:error, changeset} = + %TaskList{} + |> TaskList.create_changeset(attrs) + |> Changeset.put_change(:project_id, project_id) + |> Repo.insert + + refute changeset.valid? + assert changeset |> Map.get(:errors) |> Keyword.get(:done) + end + + test "ensures a unique 'inbox' task list per project" do + %{id: project_id} = insert(:project) + attrs = @valid_attrs |> Map.merge(%{inbox: true}) + + {:ok, _task_list} = + %TaskList{} + |> TaskList.create_changeset(attrs) + |> Changeset.put_change(:project_id, project_id) + |> Repo.insert + + {:error, changeset} = + %TaskList{} + |> TaskList.create_changeset(attrs) + |> Changeset.put_change(:project_id, project_id) + |> Repo.insert + + refute changeset.valid? + assert changeset |> Map.get(:errors) |> Keyword.get(:inbox) + end + + test "ensures a unique 'pull_requests' task list per project" do + %{id: project_id} = insert(:project) + attrs = @valid_attrs |> Map.merge(%{pull_requests: true}) + + {:ok, _task_list} = + %TaskList{} + |> TaskList.create_changeset(attrs) + |> Changeset.put_change(:project_id, project_id) + |> Repo.insert + + {:error, changeset} = + %TaskList{} + |> TaskList.create_changeset(attrs) + |> Changeset.put_change(:project_id, project_id) + |> Repo.insert - refute record.inbox + refute changeset.valid? + assert changeset |> Map.get(:errors) |> Keyword.get(:pull_requests) + end end end diff --git a/test/lib/code_corps_web/views/task_list_view_test.exs b/test/lib/code_corps_web/views/task_list_view_test.exs index f57181026..a0332d8f3 100644 --- a/test/lib/code_corps_web/views/task_list_view_test.exs +++ b/test/lib/code_corps_web/views/task_list_view_test.exs @@ -12,9 +12,11 @@ defmodule CodeCorpsWeb.TaskListViewTest do expected_json = %{ "data" => %{ "attributes" => %{ + "done" => task_list.done, "inbox" => task_list.inbox, "name" => task_list.name, "order" => 1000, + "pull-requests" => task_list.pull_requests, "inserted-at" => task_list.inserted_at, "updated-at" => task_list.updated_at, },