Skip to content

Commit 886434b

Browse files
authored
Merge pull request #1143 from code-corps/1111-sync-comments
Sync repo
2 parents a0dcb68 + 33cf7b3 commit 886434b

Some content is hidden

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

51 files changed

+2652
-149
lines changed

Diff for: config/dev.exs

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ config :code_corps, CodeCorpsWeb.Endpoint,
2626
]
2727

2828
# Do not include metadata nor timestamps in development logs
29-
config :logger, :console, format: "[$level] $message\n"
29+
config :logger,
30+
:console, format: "[$level] $message\n"
3031

3132
# Set a higher stacktrace during development. Avoid configuring such
3233
# in production as building large stacktraces may be expensive.

Diff for: lib/code_corps/accounts/accounts.ex

+41-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ defmodule CodeCorps.Accounts do
1111
Comment,
1212
GitHub.Adapters,
1313
GithubAppInstallation,
14+
GithubUser,
1415
Task,
1516
User,
1617
Repo
@@ -39,6 +40,45 @@ defmodule CodeCorps.Accounts do
3940
|> Repo.insert
4041
end
4142

43+
@doc ~S"""
44+
Creates a user record using attributes from a GitHub payload.
45+
"""
46+
@spec create_from_github_user(GithubUser.t) :: {:ok, User.t} | {:error, Changeset.t}
47+
def create_from_github_user(%GithubUser{} = github_user) do
48+
with {:ok, user} <- do_create_from_github_user(github_user) do
49+
user |> upload_github_photo_async
50+
{:ok, user}
51+
else
52+
error -> error
53+
end
54+
end
55+
56+
@spec do_create_from_github_user(GithubUser.t) :: {:ok, User.t} | {:error, Changeset.t}
57+
defp do_create_from_github_user(%GithubUser{} = github_user) do
58+
%User{}
59+
|> Changesets.create_from_github_changeset(github_user |> Adapters.User.to_user_attrs())
60+
|> Changeset.put_assoc(:github_user, github_user)
61+
|> Repo.insert
62+
end
63+
64+
@spec update_with_github_user(User.t, GithubUser.t) :: {:ok, User.t} | {:error, Changeset.t}
65+
def update_with_github_user(%User{} = user, %GithubUser{} = github_user) do
66+
with {:ok, user} <- do_update_with_github_user(user, github_user) do
67+
user |> upload_github_photo_async
68+
{:ok, user}
69+
else
70+
error -> error
71+
end
72+
end
73+
74+
@spec do_update_with_github_user(Usert.t, GithubUser.t) :: {:ok, User.t} | {:error, Changeset.t}
75+
defp do_update_with_github_user(%User{} = user, %GithubUser{} = github_user) do
76+
user
77+
|> Changesets.update_with_github_user_changeset(github_user |> Adapters.User.to_user_attrs())
78+
|> Changeset.put_assoc(:github_user, github_user)
79+
|> Repo.update
80+
end
81+
4282
@doc ~S"""
4383
Updates a user record using attributes from a GitHub payload along with the
4484
access token.
@@ -47,7 +87,7 @@ defmodule CodeCorps.Accounts do
4787
def update_from_github_oauth(%User{} = user, %{} = params, access_token) do
4888
params =
4989
params
50-
|> Adapters.User.from_github_user()
90+
|> Adapters.User.to_user()
5191
|> Map.put(:github_auth_token, access_token)
5292

5393
changeset = user |> Changesets.update_from_github_oauth_changeset(params)

Diff for: lib/code_corps/accounts/changesets.ex

+17-3
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,35 @@ defmodule CodeCorps.Accounts.Changesets do
1010
alias Ecto.Changeset
1111

1212
@doc ~S"""
13-
Casts a changeset used for creating a user account from a github user payload
13+
Casts a changeset used for creating a user account from a GitHub user payload
1414
"""
1515
@spec create_from_github_changeset(struct, map) :: Changeset.t
1616
def create_from_github_changeset(struct, %{} = params) do
1717
struct
18-
|> Changeset.change(params |> Adapters.User.from_github_user())
18+
|> Changeset.change(params |> Adapters.User.to_user())
1919
|> Changeset.put_change(:sign_up_context, "github")
2020
|> Changeset.validate_inclusion(:type, ["bot", "user"])
2121
|> RandomIconColor.generate_icon_color(:default_color)
2222
|> Changeset.unique_constraint(:email)
23+
|> Changeset.assoc_constraint(:github_user)
2324
|> unique_github_constraint()
2425
end
2526

2627
@doc ~S"""
27-
Casts a changeset used for creating a user account from a github user payload
28+
Casts a changeset used for updating a user account from a GitHub user payload
29+
"""
30+
@spec update_with_github_user_changeset(struct, map) :: Changeset.t
31+
def update_with_github_user_changeset(struct, %{} = params) do
32+
struct
33+
|> Changeset.cast(params, [:github_avatar_url, :github_id, :github_username, :type])
34+
|> ensure_email_without_overwriting(params)
35+
|> Changeset.validate_required([:github_avatar_url, :github_id, :github_username, :type])
36+
|> Changeset.unique_constraint(:email)
37+
|> unique_github_constraint()
38+
end
39+
40+
@doc ~S"""
41+
Casts a changeset used for updating a user account from a GitHub OAuth payload
2842
"""
2943
@spec update_from_github_oauth_changeset(struct, map) :: Changeset.t
3044
def update_from_github_oauth_changeset(struct, %{} = params) do

Diff for: lib/code_corps/github/adapters/comment.ex

+14-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule CodeCorps.GitHub.Adapters.Comment do
77
alias CodeCorps.{
88
Adapter.MapTransformer,
99
Comment,
10+
GithubComment,
1011
GitHub.Adapters.Utils.BodyDecorator
1112
}
1213

@@ -17,8 +18,8 @@ defmodule CodeCorps.GitHub.Adapters.Comment do
1718
]
1819

1920
@doc ~S"""
20-
Converts a Github Issue Comment payload into a set of attributes suitable to
21-
create or update a `CodeCorps.Comment`
21+
Converts a Github Issue Comment payload into a set of attributes suitable for
22+
creating or updating a `CodeCorps.Comment`
2223
"""
2324
@spec to_comment(map) :: map
2425
def to_comment(%{} = payload) do
@@ -36,6 +37,17 @@ defmodule CodeCorps.GitHub.Adapters.Comment do
3637
{:url, ["url"]}
3738
]
3839

40+
@doc ~S"""
41+
Converts a `GithubComment` record into attributes with the same keys as the
42+
GitHub API's Issue Comment
43+
"""
44+
@spec to_comment_attrs(GithubComment.t) :: map
45+
def to_comment_attrs(%GithubComment{} = github_comment) do
46+
github_comment
47+
|> Map.from_struct
48+
|> MapTransformer.transform_inverse(@github_comment_mapping)
49+
end
50+
3951
@doc ~S"""
4052
Converts a GitHub Issue Comment payload into a set of attributes suitable for
4153
creating or updating a `CodeCorps.GithubComment`

Diff for: lib/code_corps/github/adapters/issue.ex

+11
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ defmodule CodeCorps.GitHub.Adapters.Issue do
5454
payload |> MapTransformer.transform(@task_mapping)
5555
end
5656

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
63+
github_issue
64+
|> Map.from_struct
65+
|> MapTransformer.transform_inverse(@issue_mapping)
66+
end
67+
5768
@autogenerated_github_keys ~w(closed_at comments_url created_at events_url html_url id labels_url number updated_at url)
5869

5970
@doc ~S"""

Diff for: lib/code_corps/github/adapters/user.ex

+39-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ defmodule CodeCorps.GitHub.Adapters.User do
44
a `CodeCorps.Task`.
55
"""
66

7-
@mapping [
7+
alias CodeCorps.{
8+
Adapter.MapTransformer,
9+
GithubUser
10+
}
11+
12+
@user_mapping [
813
{:github_avatar_url, ["avatar_url"]},
914
{:github_id, ["id"]},
1015
{:github_username, ["login"]},
@@ -16,20 +21,48 @@ defmodule CodeCorps.GitHub.Adapters.User do
1621
Converts a Github user payload into a map of attributes suitable for creating
1722
or updating a `CodeCorps.User`
1823
19-
Any `nil` values are removed here. For example, we don't want to delete
20-
an existing email just because the GitHub payload is missing that data.
24+
Any `nil` values are removed. For example, we don't want to delete an
25+
existing email just because it's `nil` in the payload.
2126
2227
The `type` gets transformed to match our expected values for user type.
2328
"""
24-
@spec from_github_user(map) :: map
25-
def from_github_user(%{} = payload) do
29+
@spec to_user(map) :: map
30+
def to_user(%{} = payload) do
2631
payload
27-
|> CodeCorps.Adapter.MapTransformer.transform(@mapping)
32+
|> CodeCorps.Adapter.MapTransformer.transform(@user_mapping)
2833
|> Enum.reject(fn {_, v} -> is_nil(v) end)
2934
|> Map.new
3035
|> transform_type
3136
end
3237

38+
@github_user_mapping [
39+
{:avatar_url, ["avatar_url"]},
40+
{:github_id, ["id"]},
41+
{:username, ["login"]},
42+
{:email, ["email"]},
43+
{:type, ["type"]}
44+
]
45+
46+
@doc ~S"""
47+
Converts a GitHub User payload into a set of attributes used to create or
48+
update a `GithubUser` record.
49+
"""
50+
@spec to_github_user(map) :: map
51+
def to_github_user(%{} = payload) do
52+
payload |> CodeCorps.Adapter.MapTransformer.transform(@github_user_mapping)
53+
end
54+
55+
@doc ~S"""
56+
Converts a `GithubUser` into a set of attributes used to create or update a
57+
GitHub User on the GitHub API.
58+
"""
59+
@spec to_user_attrs(GithubUser.t) :: map
60+
def to_user_attrs(%GithubUser{} = github_user) do
61+
github_user
62+
|> Map.from_struct()
63+
|> MapTransformer.transform_inverse(@github_user_mapping)
64+
end
65+
3366
@spec transform_type(map) :: map
3467
defp transform_type(%{:type => "Bot"} = map), do: Map.put(map, :type, "bot")
3568
defp transform_type(%{:type => "User"} = map), do: Map.put(map, :type, "user")

Diff for: lib/code_corps/github/api/issue.ex

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule CodeCorps.GitHub.API.Issue do
55

66
alias CodeCorps.{
77
GitHub,
8+
GitHub.API,
89
GithubAppInstallation,
910
GithubIssue,
1011
GithubRepo,
@@ -19,7 +20,7 @@ defmodule CodeCorps.GitHub.API.Issue do
1920
def from_url(url, %GithubRepo{github_app_installation: %GithubAppInstallation{} = installation}) do
2021
"https://api.github.com/" <> endpoint = url
2122

22-
with opts when is_list(opts) <- GitHub.API.opts_for(installation) do
23+
with opts when is_list(opts) <- API.opts_for(installation) do
2324
GitHub.request(:get, endpoint, %{}, %{}, opts)
2425
else
2526
{:error, github_error} -> {:error, github_error}

Diff for: lib/code_corps/github/api/repository.ex

+56-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
defmodule CodeCorps.GitHub.API.Repository do
22
@moduledoc ~S"""
3-
Functions for working with issues on GitHub.
3+
Functions for retrieving a GitHub repository's issues, pull requests, and
4+
comments from the GitHub API.
45
"""
56

67
alias CodeCorps.{
@@ -10,22 +11,65 @@ defmodule CodeCorps.GitHub.API.Repository do
1011
GithubRepo,
1112
}
1213

14+
@doc ~S"""
15+
Retrieves issues for a repository
16+
17+
All pages of records are retrieved.
18+
Closed issues are included.
19+
"""
1320
@spec issues(GithubRepo.t) :: {:ok, list(map)} | {:error, GitHub.api_error_struct}
14-
def issues(%GithubRepo{github_app_installation: %GithubAppInstallation{} = installation} = github_repo) do
15-
with {:ok, access_token} <- API.Installation.get_access_token(installation),
16-
issues <- fetch_issues(github_repo, access_token)
17-
do
18-
{:ok, issues}
21+
def issues(%GithubRepo{
22+
github_app_installation: %GithubAppInstallation{
23+
github_account_login: owner
24+
} = installation,
25+
name: repo
26+
}) do
27+
with {:ok, access_token} <- API.Installation.get_access_token(installation) do
28+
"repos/#{owner}/#{repo}/issues"
29+
|> GitHub.get_all(%{}, [access_token: access_token, params: [per_page: 100, state: "all"]])
30+
|> (&{:ok, &1}).()
1931
else
2032
{:error, error} -> {:error, error}
2133
end
2234
end
2335

24-
defp fetch_issues(%GithubRepo{github_app_installation: %GithubAppInstallation{github_account_login: owner}, name: repo}, access_token) do
25-
per_page = 100
26-
path = "repos/#{owner}/#{repo}/issues"
27-
params = [per_page: per_page, state: "all"]
28-
opts = [access_token: access_token, params: params]
29-
GitHub.get_all(path, %{}, opts)
36+
@doc ~S"""
37+
Retrieves pull requests for a repository.
38+
39+
All pages of records are retrieved.
40+
"""
41+
@spec pulls(GithubRepo.t) :: {:ok, list(map)} | {:error, GitHub.api_error_struct}
42+
def pulls(%GithubRepo{
43+
github_app_installation: %GithubAppInstallation{
44+
github_account_login: owner
45+
} = installation,
46+
name: repo
47+
}) do
48+
with {:ok, access_token} <- API.Installation.get_access_token(installation) do
49+
"repos/#{owner}/#{repo}/pulls"
50+
|> GitHub.get_all(%{}, [access_token: access_token, params: [per_page: 100, state: "all"]])
51+
|> (&{:ok, &1}).()
52+
else
53+
{:error, error} -> {:error, error}
54+
end
55+
end
56+
57+
@doc ~S"""
58+
Retrieves comments from all issues in a github repository.
59+
"""
60+
@spec issue_comments(GithubRepo.t) :: {:ok, list(map)} | {:error, GitHub.api_error_struct}
61+
def issue_comments(%GithubRepo{
62+
github_app_installation: %GithubAppInstallation{
63+
github_account_login: owner
64+
} = installation,
65+
name: repo
66+
}) do
67+
with {:ok, access_token} <- API.Installation.get_access_token(installation) do
68+
"repos/#{owner}/#{repo}/issues/comments"
69+
|> GitHub.get_all(%{}, [access_token: access_token, params: [per_page: 100]])
70+
|> (&{:ok, &1}).()
71+
else
72+
{:error, error} -> {:error, error}
73+
end
3074
end
3175
end

Diff for: lib/code_corps/github/sync/comment/comment/changeset.ex

+18-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ defmodule CodeCorps.GitHub.Sync.Comment.Comment.Changeset do
1616
alias Ecto.Changeset
1717

1818
@doc ~S"""
19-
Constructs a changeset for syncing a task when processing a GitHub Comment
20-
payload
19+
Constructs a changeset for syncing a task from a GitHub API Comment payload.
2120
"""
2221
@spec build_changeset(Comment.t, map, GithubComment.t, Task.t, User.t) :: Changeset.t
2322
def build_changeset(
@@ -33,6 +32,23 @@ defmodule CodeCorps.GitHub.Sync.Comment.Comment.Changeset do
3332
comment |> update_changeset(attrs)
3433
end
3534

35+
@doc ~S"""
36+
Constructs a changeset for syncing a task from a `GithubComment` record.
37+
"""
38+
@spec build_changeset(Comment.t, GithubComment.t, Task.t, User.t) :: Changeset.t
39+
def build_changeset(
40+
%Comment{id: comment_id} = comment,
41+
%GithubComment{} = github_comment,
42+
%Task{} = task,
43+
%User{} = user) do
44+
45+
comment_attrs = github_comment |> CommentAdapter.to_comment_attrs()
46+
case is_nil(comment_id) do
47+
true -> create_changeset(comment, comment_attrs, github_comment, task, user)
48+
false -> update_changeset(comment, comment_attrs)
49+
end
50+
end
51+
3652
@create_attrs ~w(created_at markdown modified_at)a
3753
@spec create_changeset(Comment.t, map, GithubComment.t, Task.t, User.t) :: Changeset.t
3854
defp create_changeset(

0 commit comments

Comments
 (0)