Skip to content

Commit ef62b5d

Browse files
authored
Merge pull request #1095 from code-corps/refactor-events
Refactor GitHub.Event logic
2 parents cc9c36c + 4fe2bcc commit ef62b5d

File tree

14 files changed

+993
-1457
lines changed

14 files changed

+993
-1457
lines changed

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

Lines changed: 18 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -7,117 +7,45 @@ defmodule CodeCorps.GitHub.Event.IssueComment do
77
@behaviour CodeCorps.GitHub.Event.Handler
88

99
alias CodeCorps.{
10-
Comment,
1110
GitHub,
12-
GithubComment,
13-
GithubRepo,
14-
GitHub.Event.Common.RepoFinder,
15-
GitHub.Event.IssueComment.CommentDeleter,
16-
GitHub.Event.IssueComment.Validator,
17-
Repo
11+
GitHub.Event.IssueComment.Validator
1812
}
19-
alias GitHub.Sync.Comment.Comment, as: CommentCommentSyncer
20-
alias GitHub.Sync.Comment.GithubComment, as: CommentGithubCommentSyncer
21-
alias GitHub.Sync.Issue.GithubIssue, as: IssueGithubIssueSyncer
22-
alias GitHub.Sync.Issue.Task, as: IssueTaskSyncer
23-
alias GitHub.Sync.User.RecordLinker, as: UserRecordLinker
24-
alias Ecto.Multi
13+
alias GitHub.Sync.Comment, as: CommentSyncer
2514

26-
@type outcome :: {:ok, list(Comment.t)} |
27-
{:error, :unexpected_action} |
28-
{:error, :unexpected_payload} |
29-
{:error, :repository_not_found} |
30-
{:error, :validation_error_on_inserting_issue_for_task} |
31-
{:error, :validation_error_on_inserting_github_comment} |
32-
{:error, :validation_error_on_inserting_user_for_task} |
33-
{:error, :multiple_github_users_matched_same_cc_user_for_task} |
34-
{:error, :validation_error_on_inserting_user_for_comment} |
35-
{:error, :multiple_github_users_matched_same_cc_user_for_comment} |
36-
{:error, :validation_error_on_syncing_tasks} |
37-
{:error, :validation_error_on_syncing_comments} |
38-
{:error, :unexpected_transaction_outcome}
15+
@type outcome :: CommentSyncer.outcome
16+
| {:error, :unexpected_action}
17+
| {:error, :unexpected_payload}
3918

4019
@doc ~S"""
4120
Handles the "IssueComment" GitHub webhook
4221
43-
The process is as follows
22+
The process is as follows:
23+
4424
- validate the payload is structured as expected
4525
- validate the action is properly supported
46-
- match payload with affected `CodeCorps.GithubRepo` record using `CodeCorps.GitHub.Event.Common.RepoFinder`
47-
- match issue part of the payload with a `CodeCorps.User` using `CodeCorps.GitHub.Event.Issues.UserLinker`
48-
- match comment part of the payload with a `CodeCorps.User` using `CodeCorps.GitHub.Event.IssueComment.UserLinker`
49-
- for each `CodeCorps.ProjectGithubRepo` belonging to matched repo
50-
- match and update, or create a `CodeCorps.Task` on the associated `CodeCorps.Project`
51-
- match and update, or create a `CodeCorps.Comment` associated to `CodeCorps.Task`
52-
53-
If the process runs all the way through, the function will return an `:ok`
54-
tuple with a list of affected (created or updated) comments.
55-
56-
If it fails, it will instead return an `:error` tuple, where the second
57-
element is the atom indicating a reason.
26+
- sync the comment using `CodeCorps.GitHub.Sync.Comment`
5827
"""
5928
@spec handle(map) :: outcome
6029
def handle(payload) do
61-
Multi.new
62-
|> Multi.run(:payload, fn _ -> payload |> validate_payload() end)
63-
|> Multi.run(:action, fn _ -> payload |> validate_action() end)
64-
|> Multi.append(payload |> operational_multi())
65-
|> Repo.transaction
66-
|> marshall_result()
67-
end
68-
69-
@spec operational_multi(map) :: Multi.t
70-
defp operational_multi(%{"action" => action, "issue" => _, "comment" => _} = payload) when action in ~w(created edited) do
71-
Multi.new
72-
|> Multi.run(:repo, fn _ -> RepoFinder.find_repo(payload) end)
73-
|> Multi.run(:github_issue, fn %{repo: github_repo} -> github_repo |> link_issue(payload) end)
74-
|> Multi.run(:github_comment, fn %{github_issue: github_issue} -> github_issue |> sync_comment(payload) end)
75-
|> Multi.run(:issue_user, fn %{github_issue: github_issue} -> UserRecordLinker.link_to(github_issue, payload) end)
76-
|> Multi.run(:comment_user, fn %{github_comment: github_comment} -> UserRecordLinker.link_to(github_comment, payload) end)
77-
|> Multi.run(:tasks, fn %{github_issue: github_issue, issue_user: user} -> github_issue |> IssueTaskSyncer.sync_all(user, payload) end)
78-
|> Multi.run(:comments, fn %{github_comment: github_comment, tasks: tasks, comment_user: user} -> CommentCommentSyncer.sync_all(tasks, github_comment, user, payload) end)
79-
end
80-
defp operational_multi(%{"action" => "deleted"} = payload) do
81-
Multi.new
82-
|> Multi.run(:comments, fn _ -> CommentDeleter.delete_all(payload) end)
83-
end
84-
defp operational_multi(%{}), do: Multi.new
85-
86-
@spec link_issue(GithubRepo.t, map) :: {:ok, GithubIssue.t} | {:error, Ecto.Changeset.t}
87-
defp link_issue(github_repo, %{"issue" => attrs}) do
88-
IssueGithubIssueSyncer.create_or_update_issue(github_repo, attrs)
89-
end
90-
91-
@spec sync_comment(GithubIssue.t, map) :: {:ok, GithubComment.t} | {:error, Ecto.Changeset.t}
92-
defp sync_comment(github_issue, %{"comment" => attrs}) do
93-
CommentGithubCommentSyncer.create_or_update_comment(github_issue, attrs)
30+
with {:ok, :valid} <- validate_payload(payload),
31+
{:ok, :implemented} <- validate_action(payload) do
32+
CommentSyncer.sync(payload)
33+
else
34+
{:error, error} -> {:error, error}
35+
end
9436
end
9537

96-
@spec marshall_result(tuple) :: tuple
97-
defp marshall_result({:ok, %{comments: comments}}), do: {:ok, comments}
98-
defp marshall_result({:error, :payload, :invalid, _steps}), do: {:error, :unexpected_payload}
99-
defp marshall_result({:error, :action, :unexpected_action, _steps}), do: {:error, :unexpected_action}
100-
defp marshall_result({:error, :repo, :unmatched_project, _steps}), do: {:ok, []}
101-
defp marshall_result({:error, :repo, :unmatched_repository, _steps}), do: {:error, :repository_not_found}
102-
defp marshall_result({:error, :github_issue, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_issue_for_task}
103-
defp marshall_result({:error, :github_comment, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_github_comment}
104-
defp marshall_result({:error, :issue_user, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_user_for_task}
105-
defp marshall_result({:error, :issue_user, :multiple_users, _steps}), do: {:error, :multiple_github_users_matched_same_cc_user_for_task}
106-
defp marshall_result({:error, :comment_user, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_user_for_comment}
107-
defp marshall_result({:error, :comment_user, :multiple_users, _steps}), do: {:error, :multiple_github_users_matched_same_cc_user_for_comment}
108-
defp marshall_result({:error, :tasks, {_tasks, _errors}, _steps}), do: {:error, :validation_error_on_syncing_tasks}
109-
defp marshall_result({:error, :comments, {_comments, _errors}, _steps}), do: {:error, :validation_error_on_syncing_comments}
110-
defp marshall_result({:error, _errored_step, _error_response, _steps}), do: {:error, :unexpected_transaction_outcome}
111-
112-
@spec validate_payload(map) :: {:ok, :valid} | {:error, :invalid}
38+
@spec validate_payload(map) :: {:ok, :valid}
39+
| {:error, :unexpected_payload}
11340
defp validate_payload(%{} = payload) do
11441
case payload |> Validator.valid? do
11542
true -> {:ok, :valid}
116-
false -> {:error, :invalid}
43+
false -> {:error, :unexpected_payload}
11744
end
11845
end
11946

12047
@implemented_actions ~w(created edited deleted)
48+
12149
@spec validate_action(map) :: {:ok, :implemented} | {:error, :unexpected_action}
12250
defp validate_action(%{"action" => action}) when action in @implemented_actions, do: {:ok, :implemented}
12351
defp validate_action(%{}), do: {:error, :unexpected_action}

lib/code_corps/github/event/issues/issues.ex

Lines changed: 25 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,87 +9,50 @@ defmodule CodeCorps.GitHub.Event.Issues do
99

1010
alias CodeCorps.{
1111
GitHub,
12-
GitHub.Event.Common.RepoFinder,
13-
GitHub.Event.Issues.Validator,
14-
GitHub.Sync.Issue.Task,
15-
Repo,
16-
Task
12+
GitHub.Event.Issues.Validator
1713
}
18-
alias GitHub.Sync.Issue.GithubIssue, as: IssueGithubIssueSyncer
19-
alias GitHub.Sync.Issue.Task, as: IssueTaskSyncer
20-
alias GitHub.Sync.User.RecordLinker, as: UserRecordLinker
21-
alias Ecto.Multi
14+
alias GitHub.Sync.Issue, as: IssueSyncer
2215

23-
@type outcome :: {:ok, list(Task.t)} |
24-
{:error, :not_fully_implemented} |
25-
{:error, :unexpected_action} |
26-
{:error, :unexpected_payload} |
27-
{:error, :repository_not_found} |
28-
{:error, :validation_error_on_inserting_user} |
29-
{:error, :multiple_github_users_matched_same_cc_user} |
30-
{:error, :validation_error_on_syncing_tasks} |
31-
{:error, :unexpected_transaction_outcome}
16+
@type outcome :: IssueSyncer.outcome
17+
| {:error, :unexpected_action}
18+
| {:error, :not_fully_implemented}
19+
| {:error, :unexpected_payload}
3220

3321
@doc ~S"""
3422
Handles the "Issues" GitHub webhook
3523
36-
The process is as follows
24+
The process is as follows:
25+
3726
- validate the payload is structured as expected
3827
- validate the action is properly supported
39-
- match payload with affected `CodeCorps.GithubRepo` record using `CodeCorps.GitHub.Event.Common.RepoFinder`
40-
- match with a `CodeCorps.User` using `CodeCorps.GitHub.Event.Issues.UserLinker`
41-
- for each `CodeCorps.ProjectGithubRepo` belonging to matched repo
42-
- match and update, or create a `CodeCorps.Task` on the associated `CodeCorps.Project`
43-
44-
If the process runs all the way through, the function will return an `:ok`
45-
tuple with a list of affected (created or updated) tasks.
46-
47-
If it fails, it will instead return an `:error` tuple, where the second
48-
element is the atom indicating a reason.
28+
- sync the issue using `CodeCorps.GitHub.Sync.Issue`
4929
"""
5030
@spec handle(map) :: outcome
5131
def handle(payload) do
52-
Multi.new
53-
|> Multi.run(:payload, fn _ -> payload |> validate_payload() end)
54-
|> Multi.run(:action, fn _ -> payload |> validate_action() end)
55-
|> Multi.run(:repo, fn _ -> RepoFinder.find_repo(payload) end)
56-
|> Multi.run(:issue, fn %{repo: github_repo} -> link_issue(github_repo, payload) end)
57-
|> Multi.run(:user, fn %{issue: github_issue} -> UserRecordLinker.link_to(github_issue, payload) end)
58-
|> Multi.run(:tasks, fn %{issue: github_issue, user: user} -> github_issue |> IssueTaskSyncer.sync_all(user, payload) end)
59-
|> Repo.transaction
60-
|> marshall_result()
32+
with {:ok, :valid} <- validate_payload(payload),
33+
{:ok, :implemented} <- validate_action(payload) do
34+
IssueSyncer.sync(payload)
35+
else
36+
{:error, error} -> {:error, error}
37+
end
6138
end
6239

63-
@spec link_issue(GithubRepo.t, map) :: {:ok, GithubIssue.t} | {:error, Ecto.Changeset.t}
64-
defp link_issue(github_repo, %{"issue" => attrs}) do
65-
IssueGithubIssueSyncer.create_or_update_issue(github_repo, attrs)
40+
@spec validate_payload(map) :: {:ok, :valid}
41+
| {:error, :unexpected_payload}
42+
defp validate_payload(%{} = payload) do
43+
case payload |> Validator.valid? do
44+
true -> {:ok, :valid}
45+
false -> {:error, :unexpected_payload}
46+
end
6647
end
6748

68-
@spec marshall_result(tuple) :: tuple
69-
defp marshall_result({:ok, %{tasks: tasks}}), do: {:ok, tasks}
70-
defp marshall_result({:error, :payload, :invalid, _steps}), do: {:error, :unexpected_payload}
71-
defp marshall_result({:error, :action, :not_fully_implemented, _steps}), do: {:error, :not_fully_implemented}
72-
defp marshall_result({:error, :action, :unexpected_action, _steps}), do: {:error, :unexpected_action}
73-
defp marshall_result({:error, :repo, :unmatched_project, _steps}), do: {:ok, []}
74-
defp marshall_result({:error, :repo, :unmatched_repository, _steps}), do: {:error, :repository_not_found}
75-
defp marshall_result({:error, :user, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_user}
76-
defp marshall_result({:error, :user, :multiple_users, _steps}), do: {:error, :multiple_github_users_matched_same_cc_user}
77-
defp marshall_result({:error, :tasks, {_tasks, _errors}, _steps}), do: {:error, :validation_error_on_syncing_tasks}
78-
defp marshall_result({:error, _errored_step, _error_response, _steps}), do: {:error, :unexpected_transaction_outcome}
79-
8049
@implemented_actions ~w(opened closed edited reopened)
8150
@unimplemented_actions ~w(assigned unassigned milestoned demilestoned labeled unlabeled)
8251

83-
@spec validate_action(map) :: {:ok, :implemented} | {:error, :not_fully_implemented | :unexpected_action}
52+
@spec validate_action(map) :: {:ok, :implemented}
53+
| {:error, :not_fully_implemented }
54+
| {:error, :unexpected_action}
8455
defp validate_action(%{"action" => action}) when action in @implemented_actions, do: {:ok, :implemented}
8556
defp validate_action(%{"action" => action}) when action in @unimplemented_actions, do: {:error, :not_fully_implemented}
8657
defp validate_action(_payload), do: {:error, :unexpected_action}
87-
88-
@spec validate_payload(map) :: {:ok, :valid} | {:error, :invalid}
89-
defp validate_payload(%{} = payload) do
90-
case payload |> Validator.valid? do
91-
true -> {:ok, :valid}
92-
false -> {:error, :invalid}
93-
end
94-
end
9558
end

lib/code_corps/github/event/pull_request/pull_request.ex

Lines changed: 26 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -8,89 +8,51 @@ defmodule CodeCorps.GitHub.Event.PullRequest do
88
@behaviour CodeCorps.GitHub.Event.Handler
99

1010
alias CodeCorps.{
11-
GitHub.Event.Common.RepoFinder,
12-
GitHub.Event.PullRequest.Validator,
13-
Repo,
14-
Task
11+
GitHub,
12+
GitHub.Event.PullRequest.Validator
1513
}
16-
alias CodeCorps.GitHub.Sync.PullRequest.GithubPullRequest, as: GithubPullRequestSyncer
17-
alias CodeCorps.GitHub.Sync.PullRequest.Task, as: PullRequestTaskSyncer
18-
alias CodeCorps.GitHub.Sync.User.RecordLinker, as: UserRecordLinker
19-
alias Ecto.Multi
14+
alias GitHub.Sync.PullRequest, as: PullRequestSyncer
2015

21-
@type outcome :: {:ok, list(Task.t)} |
22-
{:error, :not_fully_implemented} |
23-
{:error, :unexpected_action} |
24-
{:error, :unexpected_payload} |
25-
{:error, :repository_not_found} |
26-
{:error, :validation_error_on_inserting_user} |
27-
{:error, :multiple_github_users_matched_same_cc_user} |
28-
{:error, :validation_error_on_syncing_tasks} |
29-
{:error, :unexpected_transaction_outcome}
16+
@type outcome :: PullRequestSyncer.outcome
17+
| {:error, :unexpected_action}
18+
| {:error, :not_fully_implemented}
19+
| {:error, :unexpected_payload}
3020

3121
@doc ~S"""
3222
Handles the "PullRequest" GitHub webhook
3323
34-
The process is as follows
24+
The process is as follows:
25+
3526
- validate the payload is structured as expected
3627
- validate the action is properly supported
37-
- match payload with affected `CodeCorps.GithubRepo` record using
38-
`CodeCorps.GitHub.Event.Common.RepoFinder`
39-
- match with a `CodeCorps.User` using
40-
`CodeCorps.GitHub.Event.PullRequest.UserLinker`
41-
- for each `CodeCorps.ProjectGithubRepo` belonging to matched repo
42-
- match and update, or create a `CodeCorps.Task` on the associated
43-
`CodeCorps.Project`
44-
45-
If the process runs all the way through, the function will return an `:ok`
46-
tuple with a list of affected (created or updated) tasks.
47-
48-
If it fails, it will instead return an `:error` tuple, where the second
49-
element is the atom indicating a reason.
28+
- sync the pull request using `CodeCorps.GitHub.Sync.PullRequest`
5029
"""
5130
@spec handle(map) :: outcome
5231
def handle(payload) do
53-
Multi.new
54-
|> Multi.run(:payload, fn _ -> payload |> validate_payload() end)
55-
|> Multi.run(:action, fn _ -> payload |> validate_action() end)
56-
|> Multi.run(:repo, fn _ -> RepoFinder.find_repo(payload) end)
57-
|> Multi.run(:pull_request, fn %{repo: github_repo} -> link_pull_request(github_repo, payload) end)
58-
|> Multi.run(:user, fn %{pull_request: github_pull_request} -> UserRecordLinker.link_to(github_pull_request, payload) end)
59-
|> Multi.run(:tasks, fn %{pull_request: github_pull_request, user: user} -> github_pull_request |> PullRequestTaskSyncer.sync_all(user, payload) end)
60-
|> Repo.transaction
61-
|> marshall_result()
32+
with {:ok, :valid} <- validate_payload(payload),
33+
{:ok, :implemented} <- validate_action(payload) do
34+
PullRequestSyncer.sync(payload)
35+
else
36+
{:error, error} -> {:error, error}
37+
end
6238
end
6339

64-
@spec link_pull_request(GithubRepo.t, map) :: {:ok, GithubIssue.t} | {:error, Ecto.Changeset.t}
65-
defp link_pull_request(github_repo, %{"pull_request" => attrs}) do
66-
GithubPullRequestSyncer.create_or_update_pull_request(github_repo, attrs)
40+
@spec validate_payload(map) :: {:ok, :valid}
41+
| {:error, :unexpected_payload}
42+
defp validate_payload(%{} = payload) do
43+
case payload |> Validator.valid? do
44+
true -> {:ok, :valid}
45+
false -> {:error, :unexpected_payload}
46+
end
6747
end
6848

69-
@spec marshall_result(tuple) :: tuple
70-
defp marshall_result({:ok, %{tasks: tasks}}), do: {:ok, tasks}
71-
defp marshall_result({:error, :payload, :invalid, _steps}), do: {:error, :unexpected_payload}
72-
defp marshall_result({:error, :action, :not_fully_implemented, _steps}), do: {:error, :not_fully_implemented}
73-
defp marshall_result({:error, :action, :unexpected_action, _steps}), do: {:error, :unexpected_action}
74-
defp marshall_result({:error, :repo, :unmatched_project, _steps}), do: {:ok, []}
75-
defp marshall_result({:error, :repo, :unmatched_repository, _steps}), do: {:error, :repository_not_found}
76-
defp marshall_result({:error, :user, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_user}
77-
defp marshall_result({:error, :user, :multiple_users, _steps}), do: {:error, :multiple_github_users_matched_same_cc_user}
78-
defp marshall_result({:error, :tasks, {_tasks, _errors}, _steps}), do: {:error, :validation_error_on_syncing_tasks}
79-
defp marshall_result({:error, _errored_step, _error_response, _steps}), do: {:error, :unexpected_transaction_outcome}
80-
8149
@implemented_actions ~w(opened closed edited reopened)
8250
@unimplemented_actions ~w(assigned unassigned review_requested review_request_removed labeled unlabeled)
8351

84-
@spec validate_action(map) :: {:ok, :implemented} | {:error, :not_fully_implemented | :unexpected_action}
52+
@spec validate_action(map) :: {:ok, :implemented}
53+
| {:error, :not_fully_implemented }
54+
| {:error, :unexpected_action}
8555
defp validate_action(%{"action" => action}) when action in @implemented_actions, do: {:ok, :implemented}
8656
defp validate_action(%{"action" => action}) when action in @unimplemented_actions, do: {:error, :not_fully_implemented}
8757
defp validate_action(_payload), do: {:error, :unexpected_action}
88-
89-
@spec validate_payload(map) :: {:ok, :valid} | {:error, :invalid}
90-
defp validate_payload(%{} = payload) do
91-
case payload |> Validator.valid? do
92-
true -> {:ok, :valid}
93-
false -> {:error, :invalid}
94-
end
95-
end
9658
end

0 commit comments

Comments
 (0)