Skip to content

Commit 5233619

Browse files
authored
Merge pull request #1370 from code-corps/minor-cleanup-of-github-sync
Clean up GitHub Sync with some minor documentation and spec changes
2 parents 68ac336 + 6347afc commit 5233619

File tree

46 files changed

+1348
-864
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1348
-864
lines changed

lib/code_corps/comment/service.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ defmodule CodeCorps.Comment.Service do
8484
defp create_on_github(%Comment{task: %Task{github_issue: github_issue}} = comment) do
8585
with {:ok, payload} <- comment |> GitHub.API.Comment.create(),
8686
{:ok, %GithubComment{} = github_comment} <-
87-
Sync.Comment.GithubComment.create_or_update_comment(github_issue, payload) do
87+
Sync.GithubComment.create_or_update_comment(github_issue, payload) do
8888
comment |> link_with_github_changeset(github_comment) |> Repo.update()
8989
else
9090
{:error, error} -> {:error, error}
@@ -103,7 +103,7 @@ defmodule CodeCorps.Comment.Service do
103103
) do
104104
with {:ok, payload} <- comment |> GitHub.API.Comment.update(),
105105
{:ok, %GithubComment{}} <-
106-
Sync.Comment.GithubComment.create_or_update_comment(github_issue, payload) do
106+
Sync.GithubComment.create_or_update_comment(github_issue, payload) do
107107
{:ok, comment}
108108
else
109109
{:error, error} -> {:error, error}

lib/code_corps/github/event/installation/installation.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ defmodule CodeCorps.GitHub.Event.Installation do
66

77
@behaviour CodeCorps.GitHub.Event.Handler
88

9-
alias CodeCorps.{GitHub.Sync, GitHub.Event.Installation}
9+
alias CodeCorps.{
10+
GitHub.Sync,
11+
GitHub.Event.Installation
12+
}
1013

1114

1215
@doc """
@@ -17,6 +20,7 @@ defmodule CodeCorps.GitHub.Event.Installation do
1720
1821
`CodeCorps.GitHub.Sync.installation_event/1`
1922
"""
23+
@impl CodeCorps.GitHub.Event.Handler
2024
@spec handle(map) ::
2125
Sync.installation_event_outcome() | {:error, :unexpected_payload}
2226
def handle(payload) do

lib/code_corps/github/event/installation_repositories/installation_repositories.ex

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,9 @@ defmodule CodeCorps.GitHub.Event.InstallationRepositories do
1010

1111
alias CodeCorps.{
1212
GitHub.Sync,
13-
GithubRepo,
1413
GitHub.Event.InstallationRepositories
1514
}
1615

17-
@type outcome :: {:ok, list(GithubRepo.t())} |
18-
{:error, :unmatched_installation} |
19-
{:error, :unexpected_payload} |
20-
{:error, :validation_error_on_syncing_repos} |
21-
{:error, :unexpected_transaction_outcome}
22-
2316
@doc """
2417
Handles an "InstallationRepositories" GitHub Webhook event. The event could be
2518
of subtype "added" or "removed" and is handled differently based on that.
@@ -34,7 +27,10 @@ defmodule CodeCorps.GitHub.Event.InstallationRepositories do
3427
- if the GitHub payload for a repo is not matched with a record in our
3528
database, just skip deleting it
3629
"""
37-
@spec handle(map) :: outcome
30+
@impl CodeCorps.GitHub.Event.Handler
31+
@spec handle(map) ::
32+
Sync.installation_repositories_event_outcome() |
33+
{:error, :unexpected_payload}
3834
def handle(payload) do
3935
with {:ok, :valid} <- payload |> validate_payload() do
4036
Sync.installation_repositories_event(payload)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ defmodule CodeCorps.GitHub.Event.IssueComment do
88
@behaviour CodeCorps.GitHub.Event.Handler
99

1010
alias CodeCorps.{
11-
GitHub,
11+
GitHub.Sync,
1212
GitHub.Event.IssueComment.Validator
1313
}
14-
alias GitHub.Sync
1514

1615
@doc ~S"""
1716
Handles the "IssueComment" GitHub webhook
@@ -23,7 +22,8 @@ defmodule CodeCorps.GitHub.Event.IssueComment do
2322
- sync the comment using `CodeCorps.GitHub.Sync.Comment`
2423
"""
2524
@impl CodeCorps.GitHub.Event.Handler
26-
@spec handle(map) :: {:ok, any} | {:error, atom}
25+
@spec handle(map) ::
26+
Sync.issue_comment_event_outcome() | {:error, :unexpected_payload}
2727
def handle(payload) do
2828
with {:ok, :valid} <- validate_payload(payload) do
2929
Sync.issue_comment_event(payload)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ defmodule CodeCorps.GitHub.Event.Issues do
88
@behaviour CodeCorps.GitHub.Event.Handler
99

1010
alias CodeCorps.{
11-
GitHub,
11+
GitHub.Sync,
1212
GitHub.Event.Issues.Validator
1313
}
14-
alias GitHub.Sync
1514

1615
@doc ~S"""
1716
Handles the "Issues" GitHub webhook
@@ -23,7 +22,8 @@ defmodule CodeCorps.GitHub.Event.Issues do
2322
- sync the issue using `CodeCorps.GitHub.Sync.Issue`
2423
"""
2524
@impl CodeCorps.GitHub.Event.Handler
26-
@spec handle(map) :: {:ok, any} | {:error, atom}
25+
@spec handle(map) ::
26+
Sync.issue_event_outcome() | {:error, :unexpected_payload}
2727
def handle(payload) do
2828
with {:ok, :valid} <- validate_payload(payload) do
2929
Sync.issue_event(payload)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ defmodule CodeCorps.GitHub.Event.PullRequest do
88
@behaviour CodeCorps.GitHub.Event.Handler
99

1010
alias CodeCorps.{
11-
GitHub,
11+
GitHub.Sync,
1212
GitHub.Event.PullRequest.Validator
1313
}
14-
alias GitHub.Sync
1514

1615
@doc ~S"""
1716
Handles the "PullRequest" GitHub webhook
@@ -23,7 +22,8 @@ defmodule CodeCorps.GitHub.Event.PullRequest do
2322
- sync the pull request using `CodeCorps.GitHub.Sync.PullRequest`
2423
"""
2524
@impl CodeCorps.GitHub.Event.Handler
26-
@spec handle(map) :: {:ok, any} | {:error, atom}
25+
@spec handle(map) ::
26+
Sync.pull_request_event_outcome() | {:error, :unexpected_payload}
2727
def handle(payload) do
2828
with {:ok, :valid} <- validate_payload(payload) do
2929
Sync.pull_request_event(payload)

lib/code_corps/github/sync/comment/comment/changeset.ex renamed to lib/code_corps/github/sync/comment/changeset.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
defmodule CodeCorps.GitHub.Sync.Comment.Comment.Changeset do
1+
defmodule CodeCorps.GitHub.Sync.Comment.Changeset do
22
@moduledoc ~S"""
33
In charge of building a `Changeset` to update a `Comment` with, when handling
44
a GitHub Comment payload.
@@ -16,7 +16,6 @@ defmodule CodeCorps.GitHub.Sync.Comment.Comment.Changeset do
1616
alias Ecto.Changeset
1717

1818
@create_attrs ~w(created_at markdown modified_at)a
19-
@update_attrs ~w(markdown modified_at)a
2019

2120
@doc ~S"""
2221
Constructs a changeset for creating a `CodeCorps.Comment` when syncing from a
@@ -39,6 +38,8 @@ defmodule CodeCorps.GitHub.Sync.Comment.Comment.Changeset do
3938
|> Changeset.validate_required([:markdown, :body])
4039
end
4140

41+
@update_attrs ~w(markdown modified_at)a
42+
4243
@doc ~S"""
4344
Constructs a changeset for updating a `CodeCorps.Comment` when syncing from a
4445
GitHub API Comment payload.
Lines changed: 110 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,123 @@
11
defmodule CodeCorps.GitHub.Sync.Comment do
2-
alias CodeCorps.GitHub.Sync
3-
alias Ecto.Multi
2+
@moduledoc ~S"""
3+
In charge of syncing `CodeCorps.Comment` records with a GitHub comment
4+
payload.
5+
6+
A single GitHub comment always matches a single `CodeCorps.GithubComment`, but
7+
it can match multiple `CodeCorps.Comment` records. This module handles
8+
creating or updating all those records.
9+
"""
10+
11+
import Ecto.Query
12+
13+
alias CodeCorps.{
14+
Comment,
15+
GitHub.Sync,
16+
GitHub.Utils.ResultAggregator,
17+
GithubComment,
18+
GithubIssue,
19+
GithubRepo,
20+
GithubUser,
21+
Repo,
22+
Task,
23+
User
24+
}
25+
alias Ecto.Changeset
26+
27+
@type commit_result_aggregate ::
28+
{:ok, list(Comment.t())} | {:error, {list(Comment.t()), list(Changeset.t())}}
29+
30+
@type commit_result :: {:ok, Comment.t()} | {:error, Changeset.t()}
431

532
@doc ~S"""
6-
Creates an `Ecto.Multi` intended to process a GitHub issue comment related API
7-
payload.
33+
Creates or updates a `CodeCorps.Comment` for the specified `CodeCorps.Task`.
34+
35+
When provided a `CodeCorps.Task`, a `CodeCorps.GithubComment`, a
36+
`CodeCorps.User`, and a GitHub API payload, it creates or updates a
37+
`CodeCorps.Comment` record, using the provided GitHub API
38+
payload, associated to the specified `CodeCorps.GithubComment`,
39+
`CodeCorps.Task` and `CodeCorps.User`
40+
"""
41+
@spec sync(Task.t(), GithubComment.t(), User.t()) :: commit_result()
42+
def sync(%Task{} = task, %GithubComment{} = github_comment, %User{} = user) do
43+
case find_comment(task, github_comment) do
44+
nil ->
45+
github_comment
46+
|> Sync.Comment.Changeset.create_changeset(task, user)
47+
|> Repo.insert()
48+
49+
%Comment{} = comment ->
50+
comment
51+
|> Sync.Comment.Changeset.update_changeset(github_comment)
52+
|> Repo.update()
53+
end
54+
end
55+
56+
@spec find_comment(Task.t(), GithubComment.t()) :: Comment.t() | nil
57+
defp find_comment(%Task{id: task_id}, %GithubComment{id: github_comment_id}) do
58+
query = from c in Comment,
59+
where: c.task_id == ^task_id,
60+
join: gc in GithubComment, on: c.github_comment_id == gc.id, where: gc.id == ^github_comment_id
61+
62+
query |> Repo.one()
63+
end
864

9-
Expects a partial transaction outcome with `:github_issue` and :task keys.
65+
@doc ~S"""
66+
Creates or updates `CodeCorps.Comment` records for the specified
67+
`CodeCorps.GithubRepo`.
1068
11-
Returns an `Ecto.Multi` with the follwing steps
69+
For each `CodeCorps.GithubComment` record that relates to the
70+
`CodeCorps.GithubRepo` for a given `CodeCorps.GithubRepo`:
1271
13-
- create or update a `CodeCorps.GithubComment` from the
14-
provided `CodeCorps.GithubIssue` and API payload
15-
- match the `CodeCorps.GithubComment` with a new or existing `CodeCorps.User`
16-
- create or update a `CodeCorps.Comment` using the created
17-
`CodeCorps.GithubComment`, related to the matched `CodeCorps.User` and the
18-
provided `CodeCorps.Task`
72+
- Find the related `CodeCorps.Task` record
73+
- Create or update the related `CodeCorps.Comment` record
74+
- Associate the `CodeCorps.Comment` record with the `CodeCorps.User` that
75+
relates to the `CodeCorps.GithubUser` for the `CodeCorps.GithubComment`
1976
"""
20-
@spec sync(map, map) :: Multi.t
21-
def sync(%{github_issue: github_issue, task: task}, %{} = payload) do
22-
Multi.new
23-
|> Multi.run(:github_comment, fn _ -> Sync.Comment.GithubComment.create_or_update_comment(github_issue, payload) end)
24-
|> Multi.run(:comment_user, fn %{github_comment: github_comment} -> Sync.User.RecordLinker.link_to(github_comment, payload) end)
25-
|> Multi.run(:comment, fn %{github_comment: github_comment, comment_user: user} -> Sync.Comment.Comment.sync(task, github_comment, user) end)
77+
@spec sync_github_repo(GithubRepo.t()) :: commit_result_aggregate()
78+
def sync_github_repo(%GithubRepo{} = github_repo) do
79+
preloads = [
80+
github_comments: [:github_issue, github_user: [:user]]
81+
]
82+
%GithubRepo{github_comments: github_comments} =
83+
github_repo |> Repo.preload(preloads)
84+
85+
github_comments
86+
|> Enum.map(fn %GithubComment{github_user: %GithubUser{user: %User{} = user}} = github_comment ->
87+
github_comment
88+
|> find_task(github_repo)
89+
|> sync(github_comment, user)
90+
end)
91+
|> ResultAggregator.aggregate()
92+
end
93+
94+
# TODO: can this return a nil?
95+
@spec find_task(GithubComment.t(), GithubRepo.t()) :: Task.t()
96+
defp find_task(
97+
%GithubComment{github_issue: %GithubIssue{id: github_issue_id}},
98+
%GithubRepo{project_id: project_id}) do
99+
query = from t in Task,
100+
where: t.project_id == ^project_id,
101+
join: gi in GithubIssue, on: t.github_issue_id == gi.id, where: gi.id == ^github_issue_id
102+
103+
query |> Repo.one()
26104
end
27105

28106
@doc ~S"""
29-
Creates an `Ecto.Multi` intended to delete a `CodeCorps.GithubComment`
30-
specified by `github_id`, as well as 0 to 1 `CodeCorps.Comment` records
31-
associated to `CodeCorps.GithubComment`
107+
Deletes `CodeCorps.Comment` records associated to `CodeCorps.GithubComment`
108+
with specified `github_id`
109+
110+
Since there can be 0 or 1 such records, returns `{:ok, results}` where
111+
`results` is a 1-element or blank list of deleted records.
32112
"""
33-
@spec delete(map) :: Multi.t
34-
def delete(%{"id" => github_id}) do
35-
Multi.new
36-
|> Multi.run(:deleted_comments, fn _ -> Sync.Comment.Comment.delete(github_id) end)
37-
|> Multi.run(:deleted_github_comment, fn _ -> Sync.Comment.GithubComment.delete(github_id) end)
113+
@spec delete(String.t()) :: {:ok, list(Comment.t())}
114+
def delete(github_id) do
115+
query =
116+
from c in Comment,
117+
join: gc in GithubComment, on: gc.id == c.github_comment_id, where: gc.github_id == ^github_id
118+
119+
query
120+
|> Repo.delete_all(returning: true)
121+
|> (fn {_count, comments} -> {:ok, comments} end).()
38122
end
39123
end

0 commit comments

Comments
 (0)