Skip to content

Commit 2edca91

Browse files
snewcomerjoshsmith
authored andcommitted
Added Github connect implementation
1 parent ed4f4d1 commit 2edca91

20 files changed

Lines changed: 196 additions & 26 deletions

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export CLOUDEX_API_KEY=
44
export CLOUDEX_CLOUD_NAME=
55
export CLOUDEX_SECRET=
66
export CLOUDFRONT_DOMAIN=
7+
export GITHUB_CLIENT_ID=
8+
export GITHUB_CLIENT_SECRET=
79
export POSTMARK_API_KEY=
810
export S3_BUCKET=
911
export SEGMENT_WRITE_KEY=

config/dev.exs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ config :guardian, Guardian,
4949

5050
config :code_corps, :analytics, CodeCorps.Analytics.InMemoryAPI
5151

52+
config :code_corps, :github_api, CodeCorps.Github.API
53+
5254
# Configures stripe for dev mode
5355
config :code_corps, :stripe, Stripe
5456
config :code_corps, :stripe_env, :dev
@@ -71,3 +73,7 @@ if System.get_env("CLOUDEX_API_KEY") == nil do
7173
config :code_corps, :cloudex, CloudexTest
7274
config :cloudex, api_key: "test_key", secret: "test_secret", cloud_name: "test_cloud_name"
7375
end
76+
77+
config :code_corps,
78+
github_client_id: System.get_env("GITHUB_CLIENT_ID"),
79+
github_client_secret: System.get_env("GITHUB_CLIENT_SECRET")

config/prod.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ config :code_corps,
6565
postmark_project_acceptance_template: "1447041",
6666
postmark_receipt_template: "1255222"
6767

68+
config :code_corps,
69+
github_client_id: System.get_env("GITHUB_CLIENT_ID"),
70+
github_client_secret: System.get_env("GITHUB_CLIENT_SECRET")
71+
6872
# ## SSL Support
6973
#
7074
# To get SSL working, you will need to add the `https` key

config/remote-development.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ config :code_corps,
4747
postmark_project_acceptance_template: "123",
4848
postmark_receipt_template: "123"
4949

50+
config :code_corps,
51+
github_client_id: System.get_env("GITHUB_CLIENT_ID"),
52+
github_client_secret: System.get_env("GITHUB_CLIENT_SECRET")
53+
5054
# ## SSL Support
5155
#
5256
# To get SSL working, you will need to add the `https` key

config/staging.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ config :code_corps,
6363
postmark_project_acceptance_template: "1447022",
6464
postmark_receipt_template: "1252361"
6565

66+
config :code_corps,
67+
github_client_id: System.get_env("GITHUB_CLIENT_ID"),
68+
github_client_secret: System.get_env("GITHUB_CLIENT_SECRET")
69+
6670
# ## SSL Support
6771
#
6872
# To get SSL working, you will need to add the `https` key

config/test.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ config :guardian, Guardian,
3232

3333
config :code_corps, :analytics, CodeCorps.Analytics.TestAPI
3434

35+
config :code_corps, :github_api, CodeCorps.Github.TestAPI
36+
3537
# Configures stripe for test mode
3638
config :code_corps, :stripe, CodeCorps.StripeTesting
3739
config :code_corps, :stripe_env, :test

lib/code_corps/github.ex

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,41 @@
11
defmodule CodeCorps.Github do
2-
32
alias CodeCorps.{User, Repo}
43

4+
@api Application.get_env(:code_corps, :github_api)
5+
56
@doc """
6-
Temporary function until the actual behavior is implemented.
7+
Posts code to github to receive an auth token, associates user with that
8+
auth token.
9+
10+
Accepts a third parameter, which is a custom API module, for the purposes of
11+
explicit dependency injection during testing.
12+
13+
Returns one of the following:
14+
15+
- {:ok, %CodeCorps.User{}}
16+
- {:error, %Ecto.Changeset{}}
17+
- {:error, "some_github_error"}
718
"""
8-
def connect(user, _code), do: {:ok, user}
19+
@spec connect(User.t, String.t, module) :: {:ok, User.t} | {:error, String.t}
20+
def connect(%User{} = user, code, api \\ @api) do
21+
case code |> api.connect do
22+
{:ok, github_auth_token} -> user |> associate(%{github_auth_token: github_auth_token})
23+
{:error, error} -> {:error, error}
24+
end
25+
end
26+
27+
@doc """
28+
Associates user with an auth token
929
30+
Returns one of the following:
31+
32+
- {:ok, %CodeCorps.User{}}
33+
- {:error, %Ecto.Changeset{}}
34+
"""
35+
@spec associate(User.t, map) :: {:ok, User.t} | {:error, Ecto.Changeset.t}
1036
def associate(user, params) do
1137
user
12-
|> User.github_associate_changeset(params)
38+
|> User.github_association_changeset(params)
1339
|> Repo.update()
1440
end
1541
end

lib/code_corps/github/api.ex

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
defmodule CodeCorps.Github.API do
2+
@moduledoc """
3+
The boundary module which communicates with the Github API using either
4+
direct requests, or through Tentacat
5+
"""
6+
@behaviour CodeCorps.Github.APIContract
7+
8+
@client_secret Application.get_env(:code_corps, :github_client_secret)
9+
@client_id Application.get_env(:code_corps, :github_client_id)
10+
11+
@base_connect_params %{
12+
client_id: @client_id,
13+
client_secret: @client_secret
14+
}
15+
16+
@doc """
17+
Receives a code generated through the client-side github connect process and
18+
posts it to github.
19+
20+
Returns either an {:ok, access_token}, or an {:error, error_message}.
21+
"""
22+
@spec connect(String.t) :: {:ok, String.t} | {:error, String.t}
23+
def connect(code) do
24+
with {:ok, %HTTPoison.Response{body: response}} <- code |> build_connect_params() |> do_connect(),
25+
{:ok, %{"access_token" => access_token}} <- response |> Poison.decode
26+
do
27+
{:ok, access_token}
28+
else
29+
{:ok, %{"error" => error}} -> {:error, error}
30+
end
31+
end
32+
33+
@connect_url "https://github.com/login/oauth/access_token"
34+
35+
@spec do_connect(map) :: {:ok, HTTPoison.Response.t | HTTPoison.AsyncResponse.t} | {:error, HTTPoison.Error.t}
36+
defp do_connect(params) do
37+
HTTPoison.post(@connect_url, "", [{"Accept", "application/json"}], [params: params])
38+
end
39+
40+
@spec build_connect_params(String.t) :: map
41+
defp build_connect_params(code) do
42+
@base_connect_params |> Map.put(:code, code)
43+
end
44+
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
defmodule CodeCorps.Github.APIContract do
2+
@moduledoc """
3+
Defines a contract for a github API module, listing all functions the module
4+
should implement.
5+
6+
This contract should be specified as behaviour for the default `Github.API`
7+
module, as well as any custom module we inject in tests.
8+
"""
9+
10+
@doc """
11+
Receives a code string, created in the client part of the github connect process,
12+
returns either an :ok tupple indicating a successful connect process, where
13+
the second element is the auth token string, or an :error tuple, where the
14+
second element is an error message, or a struct
15+
"""
16+
@callback connect(code :: String.t) :: {:ok, auth_token :: String.t} | {:error, error :: String.t}
17+
end

lib/code_corps/github/test_api.ex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
defmodule CodeCorps.Github.TestAPI do
2+
@moduledoc """
3+
The most basic implementation of an API module for testing. All functions
4+
here should return successes.
5+
6+
If we want to test the wrapper module, we can specify custom API modules
7+
during function calls.
8+
"""
9+
@behaviour CodeCorps.Github.APIContract
10+
11+
@spec connect(String.t) :: {:ok, String.t}
12+
def connect(code), do: send(self(), {:ok, code})
13+
end

0 commit comments

Comments
 (0)