Skip to content

Commit 437d20b

Browse files
authored
Merge pull request #1071 from code-corps/1070-github-comment
GithubComment model
2 parents 09e3eb5 + e0e5f3b commit 437d20b

27 files changed

+675
-261
lines changed

lib/code_corps/comment/service.ex

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@ defmodule CodeCorps.Comment.Service do
44
additional actions that need to be performed when such an operation happens.
55
"""
66

7-
alias CodeCorps.{Comment, GitHub, GithubRepo, Task, Repo}
7+
alias CodeCorps.{
8+
Comment,
9+
GitHub,
10+
GitHub.Event.IssueComment.CommentLinker,
11+
GithubComment,
12+
GithubIssue,
13+
Task,
14+
Repo
15+
}
816
alias Ecto.{Changeset, Multi}
917

1018
require Logger
1119

12-
@preloads [:user, task: [:github_issue, github_repo: :github_app_installation]]
20+
@preloads [:user, :github_comment, task: [:github_issue, github_repo: :github_app_installation]]
1321

1422
@doc ~S"""
1523
Creates a `Comment` record using the provided parameters
@@ -21,7 +29,7 @@ defmodule CodeCorps.Comment.Service do
2129
Multi.new
2230
|> Multi.insert(:comment, %Comment{} |> Comment.create_changeset(attributes))
2331
|> Multi.run(:preload, fn %{comment: %Comment{} = comment} -> {:ok, comment |> Repo.preload(@preloads)} end)
24-
|> Multi.run(:github, (fn %{preload: %Comment{} = comment} -> comment |> connect_to_github() end))
32+
|> Multi.run(:github, (fn %{preload: %Comment{} = comment} -> comment |> create_on_github() end))
2533
|> Repo.transaction
2634
|> marshall_result
2735
end
@@ -33,7 +41,7 @@ defmodule CodeCorps.Comment.Service do
3341
def update(%Comment{} = comment, %{} = attributes) do
3442
Multi.new
3543
|> Multi.update(:comment, comment |> Comment.update_changeset(attributes))
36-
|> Multi.run(:github, (fn %{comment: %Comment{} = comment} -> comment |> sync_to_github() end))
44+
|> Multi.run(:github, (fn %{comment: %Comment{} = comment} -> comment |> update_on_github() end))
3745
|> Repo.transaction
3846
|> marshall_result()
3947
end
@@ -47,28 +55,31 @@ defmodule CodeCorps.Comment.Service do
4755
{:error, :github}
4856
end
4957

50-
@spec connect_to_github(Comment.t) :: {:ok, Comment.t} :: {:error, GitHub.api_error_struct}
51-
defp connect_to_github(
52-
%Comment{task: %Task{github_repo: nil}} = comment), do: {:ok, comment}
53-
defp connect_to_github(
54-
%Comment{task: %Task{github_repo: %GithubRepo{}}} = comment) do
58+
@preloads [task: [:github_issue, github_repo: :github_app_installation]]
5559

56-
with {:ok, github_comment} <- comment |> GitHub.Comment.create do
60+
@spec create_on_github(Comment.t) :: {:ok, Comment.t} :: {:error, GitHub.api_error_struct}
61+
defp create_on_github(%Comment{task: %Task{github_issue_id: nil}} = comment), do: {:ok, comment}
62+
defp create_on_github(%Comment{task: %Task{github_issue: github_issue}} = comment) do
63+
with {:ok, payload} <- comment |> GitHub.Comment.create,
64+
{:ok, %GithubComment{} = github_comment} <- CommentLinker.create_or_update_comment(github_issue, payload)do
5765
comment |> link_with_github_changeset(github_comment) |> Repo.update
5866
else
5967
{:error, github_error} -> {:error, github_error}
6068
end
6169
end
6270

63-
@spec link_with_github_changeset(Comment.t, map) :: Changeset.t
64-
defp link_with_github_changeset(%Comment{} = comment, %{"id" => id}) do
65-
comment |> Changeset.change(%{github_id: id})
71+
@spec link_with_github_changeset(Comment.t, GithubComment.t) :: Changeset.t
72+
defp link_with_github_changeset(%Comment{} = comment, %GithubComment{} = github_comment) do
73+
comment |> Changeset.change(%{github_comment: github_comment})
6674
end
6775

68-
@spec sync_to_github(Comment.t) :: {:ok, Comment.t} :: {:error, GitHub.api_error_struct}
69-
defp sync_to_github(%Comment{github_id: nil} = comment), do: {:ok, comment}
70-
defp sync_to_github(%Comment{github_id: _} = comment) do
71-
with {:ok, _issue} <- comment |> Repo.preload(@preloads) |> GitHub.Comment.update do
76+
@spec update_on_github(Comment.t) :: {:ok, Comment.t} :: {:error, GitHub.api_error_struct}
77+
defp update_on_github(%Comment{github_comment_id: nil} = comment), do: {:ok, comment}
78+
defp update_on_github(%Comment{} = comment) do
79+
with %Comment{task: %Task{github_issue: %GithubIssue{} = github_issue}} = comment <- comment |> Repo.preload(@preloads),
80+
{:ok, payload} <- comment |> GitHub.Comment.update,
81+
{:ok, %GithubComment{}} <- CommentLinker.create_or_update_comment(github_issue, payload) do
82+
7283
{:ok, comment}
7384
else
7485
{:error, github_error} -> {:error, github_error}
Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
defmodule CodeCorps.GitHub.Adapters.Comment do
22
@moduledoc """
3-
Used to adapt a GitHub payload into attributes for creating or updating
4-
a `CodeCorps.Comment`.
3+
Adapts a GitHub payload into attributes for creating or updating a
4+
`CodeCorps.GithubComment` record.
55
"""
66

7-
alias CodeCorps.Comment
7+
alias CodeCorps.{
8+
Adapter.MapTransformer,
9+
Comment
10+
}
811

912
@mapping [
1013
{:created_at, ["created_at"]},
11-
{:github_id, ["id"]},
1214
{:markdown, ["body"]},
1315
{:modified_at, ["updated_at"]}
1416
]
1517

1618
@spec from_api(map) :: map
1719
def from_api(%{} = payload) do
18-
payload |> CodeCorps.Adapter.MapTransformer.transform(@mapping)
20+
payload |> MapTransformer.transform(@mapping)
1921
end
2022

2123
@autogenerated_github_keys ~w(created_at id updated_at)
@@ -24,7 +26,25 @@ defmodule CodeCorps.GitHub.Adapters.Comment do
2426
def to_api(%Comment{} = comment) do
2527
comment
2628
|> Map.from_struct
27-
|> CodeCorps.Adapter.MapTransformer.transform_inverse(@mapping)
29+
|> MapTransformer.transform_inverse(@mapping)
2830
|> Map.drop(@autogenerated_github_keys)
2931
end
32+
33+
@github_comment_mapping [
34+
{:body, ["body"]},
35+
{:github_created_at, ["created_at"]},
36+
{:github_id, ["id"]},
37+
{:github_updated_at, ["updated_at"]},
38+
{:html_url, ["html_url"]},
39+
{:url, ["url"]}
40+
]
41+
42+
@doc ~S"""
43+
Adapts the GitHub payload into attributes for creating or updating a
44+
`CodeCorps.GithubComment` record.
45+
"""
46+
@spec to_github_comment(map) :: map
47+
def to_github_comment(%{} = payload) do
48+
payload |> MapTransformer.transform(@github_comment_mapping)
49+
end
3050
end

lib/code_corps/github/comment/comment.ex

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@ defmodule CodeCorps.GitHub.Comment do
33
Handles GitHub API requests for actions on Comments
44
"""
55

6-
alias CodeCorps.{Comment, GitHub, GithubAppInstallation, GithubIssue, GithubRepo, Task, User}
6+
alias CodeCorps.{
7+
Comment,
8+
GitHub,
9+
GithubAppInstallation,
10+
GithubComment,
11+
GithubIssue,
12+
GithubRepo,
13+
Task,
14+
User
15+
}
716

817
@spec create(Comment.t) :: GitHub.response
918
def create(
@@ -50,7 +59,7 @@ defmodule CodeCorps.GitHub.Comment do
5059
@spec update_endpoint_for(Comment.t) :: String.t
5160
defp update_endpoint_for(
5261
%Comment{
53-
github_id: id,
62+
github_comment: %GithubComment{github_id: id},
5463
task: %Task{
5564
github_repo: %GithubRepo{
5665
github_account_login: owner, name: repo

lib/code_corps/github/event/issue_comment.ex

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ defmodule CodeCorps.GitHub.Event.IssueComment do
88

99
alias CodeCorps.{
1010
Comment,
11+
GithubComment,
1112
GithubRepo,
1213
GitHub.Event.Common.RepoFinder,
1314
GitHub.Event.Issues,
15+
GitHub.Event.Issues.IssueLinker,
1416
GitHub.Event.Issues.TaskSyncer,
1517
GitHub.Event.IssueComment,
18+
GitHub.Event.IssueComment.CommentLinker,
1619
GitHub.Event.IssueComment.CommentSyncer,
1720
GitHub.Event.IssueComment.CommentDeleter,
1821
Repo
@@ -24,6 +27,7 @@ defmodule CodeCorps.GitHub.Event.IssueComment do
2427
{:error, :unexpected_payload} |
2528
{:error, :repository_not_found} |
2629
{:error, :validation_error_on_inserting_issue_for_task} |
30+
{:error, :validation_error_on_inserting_github_comment} |
2731
{:error, :validation_error_on_inserting_user_for_task} |
2832
{:error, :multiple_github_users_matched_same_cc_user_for_task} |
2933
{:error, :validation_error_on_inserting_user_for_comment} |
@@ -62,28 +66,40 @@ defmodule CodeCorps.GitHub.Event.IssueComment do
6266
end
6367

6468
@spec operational_multi(map) :: Multi.t
65-
defp operational_multi(%{"action" => action, "issue" => issue_payload} = payload) when action in ~w(created edited) do
69+
defp operational_multi(%{"action" => action, "issue" => issue_payload, "comment" => comment_payload} = payload) when action in ~w(created edited) do
6670
Multi.new
6771
|> Multi.run(:repo, fn _ -> RepoFinder.find_repo(payload) end)
68-
|> Multi.run(:issue, fn %{repo: %GithubRepo{} = github_repo} -> github_repo |> Issues.IssueLinker.create_or_update_issue(issue_payload) end)
69-
|> Multi.run(:issue_user, fn %{issue: github_issue} -> github_issue |> Issues.UserLinker.find_or_create_user(payload) end)
72+
|> Multi.run(:github_issue, fn %{repo: github_repo} -> github_repo |> link_issue(payload) end)
73+
|> Multi.run(:github_comment, fn %{github_issue: github_issue} -> github_issue |> link_comment(payload) end)
74+
|> Multi.run(:issue_user, fn %{github_issue: github_issue} -> github_issue |> Issues.UserLinker.find_or_create_user(payload) end)
7075
|> Multi.run(:comment_user, fn _ -> IssueComment.UserLinker.find_or_create_user(payload) end)
71-
|> Multi.run(:tasks, fn %{issue: github_issue, issue_user: user} -> github_issue |> TaskSyncer.sync_all(user, payload) end)
72-
|> Multi.run(:comments, fn %{tasks: tasks, comment_user: user} -> CommentSyncer.sync_all(tasks, user, payload) end)
76+
|> Multi.run(:tasks, fn %{github_issue: github_issue, issue_user: user} -> github_issue |> TaskSyncer.sync_all(user, payload) end)
77+
|> Multi.run(:comments, fn %{github_comment: github_comment, tasks: tasks, comment_user: user} -> CommentSyncer.sync_all(tasks, github_comment, user, payload) end)
7378
end
7479
defp operational_multi(%{"action" => "deleted"} = payload) do
7580
Multi.new
7681
|> Multi.run(:comments, fn _ -> CommentDeleter.delete_all(payload) end)
7782
end
7883
defp operational_multi(%{}), do: Multi.new
7984

85+
@spec link_issue(GithubRepo.t, map) :: {:ok, GithubIssue.t} | {:error, Ecto.Changeset.t}
86+
defp link_issue(github_repo, %{"issue" => attrs}) do
87+
IssueLinker.create_or_update_issue(github_repo, attrs)
88+
end
89+
90+
@spec link_comment(GithubIssue.t, map) :: {:ok, GithubComment.t} | {:error, Ecto.Changeset.t}
91+
defp link_comment(github_issue, %{"comment" => attrs}) do
92+
CommentLinker.create_or_update_comment(github_issue, attrs)
93+
end
94+
8095
@spec marshall_result(tuple) :: tuple
8196
defp marshall_result({:ok, %{comments: comments}}), do: {:ok, comments}
8297
defp marshall_result({:error, :payload, :invalid, _steps}), do: {:error, :unexpected_payload}
8398
defp marshall_result({:error, :action, :unexpected_action, _steps}), do: {:error, :unexpected_action}
8499
defp marshall_result({:error, :repo, :unmatched_project, _steps}), do: {:ok, []}
85100
defp marshall_result({:error, :repo, :unmatched_repository, _steps}), do: {:error, :repository_not_found}
86-
defp marshall_result({:error, :issue, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_issue_for_task}
101+
defp marshall_result({:error, :github_issue, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_issue_for_task}
102+
defp marshall_result({:error, :github_comment, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_github_comment}
87103
defp marshall_result({:error, :issue_user, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_user_for_task}
88104
defp marshall_result({:error, :issue_user, :multiple_users, _steps}), do: {:error, :multiple_github_users_matched_same_cc_user_for_task}
89105
defp marshall_result({:error, :comment_user, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_user_for_comment}

lib/code_corps/github/event/issue_comment/changeset_builder.ex

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ defmodule CodeCorps.GitHub.Event.IssueComment.ChangesetBuilder do
66

77
alias CodeCorps.{
88
Comment,
9+
GithubComment,
910
Services.MarkdownRendererService,
1011
Task,
1112
User,
@@ -18,28 +19,41 @@ defmodule CodeCorps.GitHub.Event.IssueComment.ChangesetBuilder do
1819
Constructs a changeset for syncing a task when processing an IssueComment
1920
webhook
2021
"""
21-
@spec build_changeset(Comment.t, map, Task.t, User.t) :: Changeset.t
22-
def build_changeset(%Comment{id: nil} = comment, %{"comment" => attrs}, %Task{} = task, %User{} = user) do
23-
comment |> create_changeset(attrs, task, user)
22+
@spec build_changeset(Comment.t, map, GithubComment.t, Task.t, User.t) :: Changeset.t
23+
def build_changeset(
24+
%Comment{id: nil} = comment,
25+
%{"comment" => attrs},
26+
%GithubComment{} = github_comment,
27+
%Task{} = task,
28+
%User{} = user) do
29+
30+
comment |> create_changeset(attrs, github_comment, task, user)
2431
end
25-
def build_changeset(%Comment{} = comment, %{"comment" => attrs}, %Task{}, %User{}) do
32+
def build_changeset(%Comment{} = comment, %{"comment" => attrs}, %GithubComment{}, %Task{}, %User{}) do
2633
comment |> update_changeset(attrs)
2734
end
2835

29-
@create_attrs ~w(created_at github_id markdown modified_at)a
30-
@spec create_changeset(Comment.t, map, Task.t, User.t) :: Changeset.t
31-
defp create_changeset(%Comment{} = comment, %{} = attrs, %Task{} = task, %User{} = user) do
36+
@create_attrs ~w(created_at markdown modified_at)a
37+
@spec create_changeset(Comment.t, map, GithubComment.t, Task.t, User.t) :: Changeset.t
38+
defp create_changeset(
39+
%Comment{} = comment,
40+
%{} = attrs,
41+
%GithubComment{} = github_comment,
42+
%Task{} = task,
43+
%User{} = user) do
44+
3245
comment
3346
|> Changeset.cast(CommentAdapter.from_api(attrs), @create_attrs)
3447
|> MarkdownRendererService.render_markdown_to_html(:markdown, :body)
3548
|> Changeset.put_change(:created_from, "github")
3649
|> Changeset.put_change(:modified_from, "github")
3750
|> Changeset.put_assoc(:task, task)
51+
|> Changeset.put_assoc(:github_comment, github_comment)
3852
|> Changeset.put_change(:user, user)
3953
|> Changeset.validate_required([:markdown, :body])
4054
end
4155

42-
@update_attrs ~w(github_id markdown modified_at)a
56+
@update_attrs ~w(markdown modified_at)a
4357
@spec update_changeset(Comment.t, map) :: Changeset.t
4458
defp update_changeset(%Comment{} = comment, %{} = attrs) do
4559
comment

lib/code_corps/github/event/issue_comment/comment_deleter.ex

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
defmodule CodeCorps.GitHub.Event.IssueComment.CommentDeleter do
22
alias CodeCorps.{
33
Comment,
4+
GithubComment,
45
Repo
56
}
67

@@ -12,8 +13,11 @@ defmodule CodeCorps.GitHub.Event.IssueComment.CommentDeleter do
1213
"""
1314
@spec delete_all(map) :: {:ok, list(Comment.t)}
1415
def delete_all(%{"comment" => %{"id" => github_id}}) do
15-
Comment
16-
|> where([c], c.github_id == ^github_id)
16+
query =
17+
from c in Comment,
18+
join: gc in GithubComment, on: gc.id == c.github_comment_id, where: gc.github_id == ^github_id
19+
20+
query
1721
|> Repo.delete_all(returning: true)
1822
|> (fn {_count, comments} -> {:ok, comments} end).()
1923
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
defmodule CodeCorps.GitHub.Event.IssueComment.CommentLinker do
2+
@moduledoc ~S"""
3+
In charge of finding a issue to link with a Task when processing an Issues
4+
webhook.
5+
6+
The only entry point is `create_or_update_issue/1`.
7+
"""
8+
9+
alias CodeCorps.{
10+
GitHub.Adapters,
11+
GithubComment,
12+
GithubIssue,
13+
Repo
14+
}
15+
16+
@typep linking_result :: {:ok, GithubComment.t} | {:error, Ecto.Changeset.t}
17+
18+
@doc ~S"""
19+
Finds or creates a `CodeCorps.GithubComment` using the data in a GitHub
20+
IssueComment payload.
21+
22+
The process is as follows:
23+
24+
- Search for the issue in our database with the payload data.
25+
- If found, update it with payload data
26+
- If not found, create it from payload data
27+
28+
`CodeCorps.GitHub.Adapters.Comment.to_github_comment/1` is used to adapt the
29+
payload data.
30+
"""
31+
@spec create_or_update_comment(GithubIssue.t, map) :: linking_result
32+
def create_or_update_comment(%GithubIssue{} = github_issue, %{"id" => github_comment_id} = attrs) do
33+
params = Adapters.Comment.to_github_comment(attrs)
34+
35+
case Repo.get_by(GithubComment, github_id: github_comment_id) do
36+
nil -> create_comment(github_issue, params)
37+
%GithubComment{} = github_comment -> github_comment |> update_issue(params)
38+
end
39+
end
40+
41+
defp create_comment(%GithubIssue{id: github_issue_id}, params) do
42+
params = Map.put(params, :github_issue_id, github_issue_id)
43+
44+
%GithubComment{}
45+
|> GithubComment.create_changeset(params)
46+
|> Repo.insert
47+
end
48+
49+
defp update_issue(%GithubComment{} = github_comment, params) do
50+
github_comment
51+
|> GithubComment.update_changeset(params)
52+
|> Repo.update
53+
end
54+
end

0 commit comments

Comments
 (0)