Skip to content

Commit 0e6eb76

Browse files
authored
Merge pull request #1158 from code-corps/1053-assign-task-lists-and-archived-during-sync
Assign task lists and archived during sync
2 parents 1ba8911 + 339d4e2 commit 0e6eb76

File tree

14 files changed

+250
-117
lines changed

14 files changed

+250
-117
lines changed

lib/code_corps/github/adapters/issue.ex

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ defmodule CodeCorps.GitHub.Adapters.Issue do
1111
Task
1212
}
1313

14-
@issue_mapping [
14+
@github_payload_to_github_issue_mapping [
1515
{:body, ["body"]},
1616
{:closed_at, ["closed_at"]},
1717
{:comments_url, ["comments_url"]},
@@ -34,35 +34,34 @@ defmodule CodeCorps.GitHub.Adapters.Issue do
3434
"""
3535
@spec to_issue(map) :: map
3636
def to_issue(%{} = payload) do
37-
payload |> MapTransformer.transform(@issue_mapping)
37+
payload |> MapTransformer.transform(@github_payload_to_github_issue_mapping)
3838
end
3939

40-
@task_mapping [
40+
@github_payload_to_task_mapping [
4141
{:created_at, ["created_at"]},
4242
{:markdown, ["body"]},
4343
{:modified_at, ["updated_at"]},
4444
{:status, ["state"]},
4545
{:title, ["title"]}
4646
]
4747

48+
@github_issue_to_task_mapping [
49+
{:created_at, [:github_created_at]},
50+
{:markdown, [:body]},
51+
{:modified_at, [:github_updated_at]},
52+
{:status, [:state]},
53+
{:title, [:title]}
54+
]
55+
4856
@doc ~S"""
4957
Converts a GitHub Issue payload into a set of attributes used to create or
5058
update a `Task` record.
5159
"""
52-
@spec to_task(map) :: map
53-
def to_task(%{} = payload) do
54-
payload |> MapTransformer.transform(@task_mapping)
55-
end
56-
57-
@doc ~S"""
58-
Converts a `GithubIssue` record into attributes with the same keys as the
59-
GitHub API's Issue
60-
"""
61-
@spec to_issue_attrs(GithubIssue.t) :: map
62-
def to_issue_attrs(%GithubIssue{} = github_issue) do
60+
@spec to_task(GithubIssue.t) :: map
61+
def to_task(%GithubIssue{} = github_issue) do
6362
github_issue
6463
|> Map.from_struct
65-
|> MapTransformer.transform_inverse(@issue_mapping)
64+
|> MapTransformer.transform(@github_issue_to_task_mapping)
6665
end
6766

6867
@autogenerated_github_keys ~w(closed_at comments_url created_at events_url html_url id labels_url number updated_at url)
@@ -75,14 +74,14 @@ defmodule CodeCorps.GitHub.Adapters.Issue do
7574
def to_api(%GithubIssue{} = github_issue) do
7675
github_issue
7776
|> Map.from_struct
78-
|> MapTransformer.transform_inverse(@issue_mapping)
77+
|> MapTransformer.transform_inverse(@github_payload_to_github_issue_mapping)
7978
|> Map.drop(@autogenerated_github_keys)
8079
|> BodyDecorator.add_code_corps_header(github_issue)
8180
end
8281
def to_api(%Task{} = task) do
8382
task
8483
|> Map.from_struct
85-
|> MapTransformer.transform_inverse(@task_mapping)
84+
|> MapTransformer.transform_inverse(@github_payload_to_task_mapping)
8685
|> Map.drop(@autogenerated_github_keys)
8786
|> BodyDecorator.add_code_corps_header(task)
8887
end

lib/code_corps/github/sync/issue/issue.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ defmodule CodeCorps.GitHub.Sync.Issue do
4040
Multi.new
4141
|> Multi.run(:github_issue, fn _ -> link_issue({github_repo, github_pull_request}, payload) end)
4242
|> Multi.run(:issue_user, fn %{github_issue: github_issue} -> UserRecordLinker.link_to(github_issue, payload) end)
43-
|> Multi.run(:tasks, fn %{github_issue: github_issue, issue_user: user} -> github_issue |> IssueTaskSyncer.sync_all(user, payload) end)
43+
|> Multi.run(:tasks, fn %{github_issue: github_issue, issue_user: user} -> github_issue |> IssueTaskSyncer.sync_all(user) end)
4444
end
4545

4646
@spec link_issue({GithubRepo.t, GithubPullRequest.t}, map) :: {:ok, GithubIssue.t} | {:error, Ecto.Changeset.t}

lib/code_corps/github/sync/issue/task/changeset.ex

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,77 +23,106 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task.Changeset do
2323
2424
The changeset can be used to create or update a `Task`
2525
"""
26-
@spec build_changeset(Task.t, map, GithubIssue.t, ProjectGithubRepo.t, User.t) :: Changeset.t
27-
def build_changeset(
28-
%Task{id: task_id} = task,
29-
%{} = issue_attrs,
30-
%GithubIssue{} = github_issue,
31-
%ProjectGithubRepo{} = project_github_repo,
32-
%User{} = user) do
33-
34-
case is_nil(task_id) do
35-
true -> create_changeset(task, issue_attrs, github_issue, project_github_repo, user)
36-
false -> update_changeset(task, issue_attrs)
37-
end
38-
end
39-
4026
@spec build_changeset(Task.t, GithubIssue.t, ProjectGithubRepo.t, User.t) :: Changeset.t
4127
def build_changeset(
4228
%Task{id: task_id} = task,
4329
%GithubIssue{} = github_issue,
4430
%ProjectGithubRepo{} = project_github_repo,
4531
%User{} = user) do
4632

47-
issue_attrs = github_issue |> IssueAdapter.to_issue_attrs()
4833
case is_nil(task_id) do
49-
true -> create_changeset(task, issue_attrs, github_issue, project_github_repo, user)
50-
false -> update_changeset(task, issue_attrs)
34+
true -> create_changeset(task, github_issue, project_github_repo, user)
35+
false -> update_changeset(task, github_issue, project_github_repo)
5136
end
5237
end
5338

5439
@create_attrs ~w(created_at markdown modified_at status title)a
55-
@spec create_changeset(Task.t, map, GithubIssue.t, ProjectGithubRepo.t, User.t) :: Changeset.t
40+
@spec create_changeset(Task.t, GithubIssue.t, ProjectGithubRepo.t, User.t) :: Changeset.t
5641
defp create_changeset(
5742
%Task{} = task,
58-
%{} = issue_attrs,
59-
%GithubIssue{id: github_issue_id},
43+
%GithubIssue{id: github_issue_id} = github_issue,
6044
%ProjectGithubRepo{project_id: project_id, github_repo_id: github_repo_id},
6145
%User{id: user_id}) do
6246

63-
%TaskList{id: task_list_id} =
64-
TaskList |> Repo.get_by(project_id: project_id, inbox: true)
65-
6647
task
67-
|> Changeset.cast(IssueAdapter.to_task(issue_attrs), @create_attrs)
48+
|> Changeset.cast(github_issue |> IssueAdapter.to_task, @create_attrs)
6849
|> MarkdownRendererService.render_markdown_to_html(:markdown, :body)
6950
|> Changeset.put_change(:created_from, "github")
7051
|> Changeset.put_change(:modified_from, "github")
7152
|> Changeset.put_change(:github_issue_id, github_issue_id)
7253
|> Changeset.put_change(:github_repo_id, github_repo_id)
7354
|> Changeset.put_change(:project_id, project_id)
74-
|> Changeset.put_change(:task_list_id, task_list_id)
55+
|> assign_task_list(github_issue, project_id)
7556
|> Changeset.put_change(:user_id, user_id)
7657
|> Changeset.validate_required([:project_id, :task_list_id, :title, :user_id])
7758
|> Changeset.assoc_constraint(:github_issue)
7859
|> Changeset.assoc_constraint(:github_repo)
7960
|> Changeset.assoc_constraint(:project)
80-
|> Changeset.assoc_constraint(:task_list)
8161
|> Changeset.assoc_constraint(:user)
82-
|> Task.order_task()
62+
|> maybe_archive()
63+
|> Task.handle_archived()
8364
end
8465

8566
@update_attrs ~w(markdown modified_at status title)a
86-
@spec update_changeset(Task.t, map) :: Changeset.t
87-
defp update_changeset(%Task{} = task, %{} = issue_attrs) do
67+
@spec update_changeset(Task.t, GithubIssue.t, ProjectGithubRepo.t) :: Changeset.t
68+
defp update_changeset(
69+
%Task{} = task,
70+
%GithubIssue{} = github_issue,
71+
%ProjectGithubRepo{project_id: project_id}) do
8872
task
89-
|> Changeset.cast(IssueAdapter.to_task(issue_attrs), @update_attrs)
73+
|> Changeset.cast(github_issue |> IssueAdapter.to_task, @update_attrs)
9074
|> MarkdownRendererService.render_markdown_to_html(:markdown, :body)
9175
|> Changeset.put_change(:modified_from, "github")
9276
|> TimeValidator.validate_time_not_before(:modified_at)
77+
|> assign_task_list(github_issue, project_id)
9378
|> Changeset.validate_required([:project_id, :title, :user_id])
9479
|> Changeset.assoc_constraint(:github_repo)
9580
|> Changeset.assoc_constraint(:project)
9681
|> Changeset.assoc_constraint(:user)
97-
|> Task.order_task()
82+
|> maybe_archive()
83+
|> Task.handle_archived()
84+
end
85+
86+
@spec assign_task_list(Changeset.t, GithubIssue.t, integer) :: Changeset.t
87+
defp assign_task_list(
88+
%Changeset{} = changeset, %GithubIssue{} = github_issue, project_id)
89+
do
90+
%TaskList{id: task_list_id} =
91+
github_issue
92+
|> get_task_list_type()
93+
|> get_task_list(project_id)
94+
95+
changeset
96+
|> Changeset.put_change(:task_list_id, task_list_id)
97+
|> Changeset.assoc_constraint(:task_list)
98+
end
99+
100+
@spec get_task_list_type(GithubIssue.t) :: atom
101+
defp get_task_list_type(%GithubIssue{state: "closed"}), do: :done
102+
defp get_task_list_type(%GithubIssue{state: "open", github_pull_request_id: pr_id})
103+
when not is_nil(pr_id), do: :pull_requests
104+
defp get_task_list_type(%GithubIssue{state: "open"}), do: :inbox
105+
106+
@spec get_task_list(atom, integer) :: TaskList.t
107+
defp get_task_list(:done, project_id) do
108+
TaskList |> Repo.get_by(project_id: project_id, done: true)
109+
end
110+
defp get_task_list(:inbox, project_id) do
111+
TaskList |> Repo.get_by(project_id: project_id, inbox: true)
112+
end
113+
defp get_task_list(:pull_requests, project_id) do
114+
TaskList |> Repo.get_by(project_id: project_id, pull_requests: true)
115+
end
116+
117+
@spec maybe_archive(Changeset.t) :: Changeset.t
118+
defp maybe_archive(%Changeset{} = changeset) do
119+
modified_at = changeset |> Changeset.get_field(:modified_at)
120+
status = changeset |> Changeset.get_field(:status)
121+
122+
case {status, Timex.now |> Timex.diff(modified_at, :days)} do
123+
{"closed", days_since_modified} when days_since_modified > 30 ->
124+
changeset |> Changeset.put_change(:archived, true)
125+
_ -> changeset
126+
end
98127
end
99128
end

lib/code_corps/github/sync/issue/task/task.ex

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,18 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task do
1616
{:error, {list(Task.t), list(Changeset.t)}}
1717

1818
@doc """
19-
When provided a `CodeCorps.GithubIssue`, a `CodeCorps.User` and a GitHub API
20-
payload, for each `CodeCorps.Project` associated to that
21-
`CodeCorps.GithubRepo` via a `CodeCorps.ProjectGithubRepo`, it
22-
creates or updates a `CodeCorps.Task`.
19+
When provided a `GithubIssue` and a `User`, for each `Project` associated to
20+
that `GithubRepo` via a `ProjectGithubRepo`, it creates or updates a `Task`.
2321
"""
24-
@spec sync_all(GithubIssue.t, User.t, map) :: {:ok, list(Task.t)}
25-
def sync_all(%GithubIssue{} = github_issue, %User{} = user, %{} = payload) do
22+
@spec sync_all(GithubIssue.t, User.t) :: {:ok, list(Task.t)}
23+
def sync_all(%GithubIssue{} = github_issue, %User{} = user) do
2624

2725
%GithubIssue{
2826
github_repo: %GithubRepo{project_github_repos: project_github_repos}
2927
} = github_issue |> Repo.preload(github_repo: :project_github_repos)
3028

3129
project_github_repos
32-
|> Enum.map(&sync(github_issue, &1, user, payload))
30+
|> Enum.map(&sync(github_issue, &1, user))
3331
|> ResultAggregator.aggregate
3432
end
3533

@@ -45,17 +43,18 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task do
4543
"""
4644
def sync_project_github_repo(%ProjectGithubRepo{github_repo: %GithubRepo{} = _} = project_github_repo) do
4745
%ProjectGithubRepo{
48-
github_repo: %GithubRepo{
49-
github_issues: github_issues
50-
}
46+
github_repo: %GithubRepo{github_issues: github_issues}
5147
} = project_github_repo |> Repo.preload([:project, github_repo: [github_issues: [github_user: [:user]]]])
5248

5349
github_issues
5450
|> Enum.map(&find_or_create_task(&1, project_github_repo))
5551
|> ResultAggregator.aggregate
5652
end
5753

58-
defp find_or_create_task(%GithubIssue{github_user: %GithubUser{user: %User{} = user}} = github_issue, %ProjectGithubRepo{} = project_github_repo) do
54+
defp find_or_create_task(
55+
%GithubIssue{github_user: %GithubUser{user: %User{} = user}} = github_issue,
56+
%ProjectGithubRepo{} = project_github_repo) do
57+
5958
sync(github_issue, project_github_repo, user)
6059
end
6160

@@ -67,14 +66,6 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task do
6766
|> Repo.insert_or_update()
6867
end
6968

70-
@spec sync(GithubIssue.t, ProjectGithubRepo.t, User.t, map) :: {:ok, ProjectGithubRepo.t} | {:error, Changeset.t}
71-
defp sync(%GithubIssue{} = github_issue, %ProjectGithubRepo{} = project_github_repo, %User{} = user, %{} = payload) do
72-
project_github_repo
73-
|> find_or_init_task(github_issue)
74-
|> TaskChangeset.build_changeset(payload, github_issue, project_github_repo, user)
75-
|> Repo.insert_or_update()
76-
end
77-
7869
@spec find_or_init_task(ProjectGithubRepo.t, GithubIssue.t) :: Task.t
7970
defp find_or_init_task(
8071
%ProjectGithubRepo{project_id: project_id},
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
defmodule CodeCorps.Repo.Migrations.ArchiveOutdatedTasks do
2+
use Ecto.Migration
3+
4+
import Ecto.Query
5+
6+
alias CodeCorps.Repo
7+
8+
def up do
9+
from(
10+
t in "tasks",
11+
where: t.status == "closed",
12+
where: date_add(t.modified_at, 30, "day") > ^Date.utc_today,
13+
update: [set: [archived: true, task_list_id: nil]]
14+
) |> Repo.update_all([])
15+
end
16+
17+
def down do
18+
# no-op
19+
end
20+
end

priv/repo/structure.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3867,5 +3867,5 @@ ALTER TABLE ONLY users
38673867
-- PostgreSQL database dump complete
38683868
--
38693869

3870-
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);
3870+
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), (20171106200036);
38713871

test/lib/code_corps/github/adapters/issue_test.exs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ defmodule CodeCorps.GitHub.Adapters.IssueTest do
44
use ExUnit.Case, async: true
55

66
import CodeCorps.GitHub.TestHelpers
7+
import CodeCorps.Factories
78

89
alias CodeCorps.{GitHub.Adapters, GithubIssue, Task}
910

@@ -31,15 +32,15 @@ defmodule CodeCorps.GitHub.Adapters.IssueTest do
3132
end
3233

3334
describe "to_task/1" do
34-
test "maps api payload correctly" do
35-
%{"issue" => payload} = load_event_fixture("issues_opened")
35+
test "maps github issue correctly" do
36+
github_issue = build(:github_issue)
3637

37-
assert Adapters.Issue.to_task(payload) == %{
38-
created_at: payload["created_at"],
39-
markdown: payload["body"],
40-
modified_at: payload["updated_at"],
41-
status: payload["state"],
42-
title: payload["title"]
38+
assert github_issue |> Adapters.Issue.to_task == %{
39+
created_at: github_issue.github_created_at,
40+
markdown: github_issue.body,
41+
modified_at: github_issue.github_updated_at,
42+
status: github_issue.state,
43+
title: github_issue.title
4344
}
4445
end
4546
end

test/lib/code_corps/github/event/issues/issues_test.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ defmodule CodeCorps.GitHub.Event.IssuesTest do
3333

3434
github_repo = insert(:github_repo, github_id: repo_github_id)
3535
%{project: project} = insert(:project_github_repo, github_repo: github_repo)
36+
insert(:task_list, project: project, done: true)
3637
insert(:task_list, project: project, inbox: true)
38+
insert(:task_list, project: project, pull_requests: true)
3739

3840
{:ok, tasks} = Issues.handle(@payload)
3941

test/lib/code_corps/github/event/pull_request/pull_request_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ defmodule CodeCorps.GitHub.Event.PullRequestTest do
3434

3535
github_repo = insert(:github_repo, github_id: repo_github_id)
3636
%{project: project} = insert(:project_github_repo, github_repo: github_repo)
37-
insert(:task_list, project: project, inbox: true)
37+
insert(:task_list, project: project, pull_requests: true)
3838

3939
{:ok, %{github_pull_request: github_pull_request}} = PullRequest.handle(@payload)
4040

test/lib/code_corps/github/sync/issue/issue_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ defmodule CodeCorps.GitHub.Sync.IssueTest do
132132

133133
project_ids |> Enum.each(fn project_id ->
134134
project = Project |> Repo.get_by(id: project_id)
135-
insert(:task_list, project: project, inbox: true)
135+
insert(:task_list, project: project, pull_requests: true)
136136
end)
137137

138138
%{id: existing_task_id} =

0 commit comments

Comments
 (0)