forked from code-corps/code-corps-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstallation.ex
More file actions
101 lines (86 loc) · 3.73 KB
/
installation.ex
File metadata and controls
101 lines (86 loc) · 3.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
defmodule CodeCorps.GitHub.API.Installation do
@moduledoc """
Functions for performing installation actions on the GitHub API.
"""
alias CodeCorps.{
GitHub,
GithubAppInstallation,
Repo
}
alias Ecto.Changeset
@doc """
List repositories that are accessible to the authenticated installation.
All pages of records are retrieved.
https://developer.github.com/v3/apps/installations/#list-repositories
"""
@spec repositories(GithubAppInstallation.t) :: {:ok, list(map)} | {:error, GitHub.paginated_endpoint_error}
def repositories(%GithubAppInstallation{} = installation) do
with {:ok, access_token} <- installation |> get_access_token(),
{:ok, responses} <- access_token |> fetch_repositories() do
{:ok, responses |> extract_repositories}
else
{:error, error} -> {:error, error}
end
end
@spec fetch_repositories(String.t) :: {:ok, list(map)} | {:error, GitHub.paginated_endpoint_error}
defp fetch_repositories(access_token) do
"installation/repositories"
|> GitHub.get_all(%{}, [access_token: access_token, params: [per_page: 100]])
end
@spec extract_repositories(list(map)) :: list(map)
defp extract_repositories(responses) do
responses
|> Enum.map(&Map.get(&1, "repositories"))
|> List.flatten
end
@doc """
Get the access token for the installation.
Returns either the current access token stored in the database because
it has not yet expired, or makes a request to the GitHub API for a new
access token using the GitHub App's JWT.
https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/#authenticating-as-an-installation
"""
@spec get_access_token(GithubAppInstallation.t) :: {:ok, String.t} | {:error, GitHub.api_error_struct} | {:error, Changeset.t}
def get_access_token(%GithubAppInstallation{access_token: token, access_token_expires_at: expires_at} = installation) do
case token_expired?(expires_at) do
true -> installation |> refresh_token()
false -> {:ok, token} # return the existing token
end
end
@doc """
Refreshes the access token for the installation.
Makes a request to the GitHub API for a new access token using the GitHub
App's JWT.
https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/#authenticating-as-an-installation
"""
@spec refresh_token(GithubAppInstallation.t) :: {:ok, String.t} | {:error, GitHub.api_error_struct} | {:error, Changeset.t}
def refresh_token(%GithubAppInstallation{github_id: installation_id} = installation) do
endpoint = "installations/#{installation_id}/access_tokens"
with {:ok, %{"token" => token, "expires_at" => expires_at}} <-
GitHub.integration_request(:post, endpoint, %{}, %{}, []),
{:ok, %GithubAppInstallation{}} <-
update_token(installation, token, expires_at)
do
{:ok, token}
else
{:error, error} -> {:error, error}
end
end
@spec update_token(GithubAppInstallation.t, String.t, String.t) :: {:ok, GithubAppInstallation.t} | {:error, Changeset.t}
defp update_token(%GithubAppInstallation{} = installation, token, expires_at) do
installation
|> GithubAppInstallation.access_token_changeset(%{access_token: token, access_token_expires_at: expires_at})
|> Repo.update
end
@doc false
@spec token_expired?(String.t | DateTime.t | nil) :: true | false
def token_expired?(expires_at) when is_binary(expires_at) do
expires_at
|> Timex.parse!("{ISO:Extended:Z}")
|> token_expired?()
end
def token_expired?(%DateTime{} = expires_at) do
Timex.before?(expires_at, Timex.now)
end
def token_expired?(nil), do: true
end