diff --git a/.env.example b/.env.example index 4e3766b95..1ae8373fd 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,8 @@ export CLOUDEX_API_KEY= export CLOUDEX_CLOUD_NAME= export CLOUDEX_SECRET= export CLOUDFRONT_DOMAIN= +export GITHUB_CLIENT_ID= +export GITHUB_CLIENT_SECRET= export POSTMARK_API_KEY= export S3_BUCKET= export SEGMENT_WRITE_KEY= diff --git a/config/dev.exs b/config/dev.exs index bde934908..81fc91e26 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -49,6 +49,8 @@ config :guardian, Guardian, config :code_corps, :analytics, CodeCorps.Analytics.InMemoryAPI +config :code_corps, :github_api, CodeCorps.Github.API + # Configures stripe for dev mode config :code_corps, :stripe, Stripe config :code_corps, :stripe_env, :dev @@ -70,3 +72,7 @@ if System.get_env("CLOUDEX_API_KEY") == nil do config :code_corps, :cloudex, CloudexTest config :cloudex, api_key: "test_key", secret: "test_secret", cloud_name: "test_cloud_name" end + +config :code_corps, + github_client_id: System.get_env("GITHUB_CLIENT_ID"), + github_client_secret: System.get_env("GITHUB_CLIENT_SECRET") diff --git a/config/prod.exs b/config/prod.exs index 2d6e7fb86..09adf76c3 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -64,6 +64,10 @@ config :code_corps, postmark_project_acceptance_template: "1447041", postmark_receipt_template: "1255222" +config :code_corps, + github_client_id: System.get_env("GITHUB_CLIENT_ID"), + github_client_secret: System.get_env("GITHUB_CLIENT_SECRET") + # ## SSL Support # # To get SSL working, you will need to add the `https` key diff --git a/config/remote-development.exs b/config/remote-development.exs index 32ac75872..a2f99a576 100644 --- a/config/remote-development.exs +++ b/config/remote-development.exs @@ -46,6 +46,10 @@ config :code_corps, postmark_project_acceptance_template: "123", postmark_receipt_template: "123" +config :code_corps, + github_client_id: System.get_env("GITHUB_CLIENT_ID"), + github_client_secret: System.get_env("GITHUB_CLIENT_SECRET") + # ## SSL Support # # To get SSL working, you will need to add the `https` key diff --git a/config/staging.exs b/config/staging.exs index c6aa626e2..2543354b1 100644 --- a/config/staging.exs +++ b/config/staging.exs @@ -62,6 +62,10 @@ config :code_corps, postmark_project_acceptance_template: "1447022", postmark_receipt_template: "1252361" +config :code_corps, + github_client_id: System.get_env("GITHUB_CLIENT_ID"), + github_client_secret: System.get_env("GITHUB_CLIENT_SECRET") + # ## SSL Support # # To get SSL working, you will need to add the `https` key diff --git a/config/test.exs b/config/test.exs index 2f03e3825..47234d45d 100644 --- a/config/test.exs +++ b/config/test.exs @@ -32,6 +32,8 @@ config :guardian, Guardian, config :code_corps, :analytics, CodeCorps.Analytics.TestAPI +config :code_corps, :github_api, CodeCorps.Github.TestAPI + # Configures stripe for test mode config :code_corps, :stripe, CodeCorps.StripeTesting config :code_corps, :stripe_env, :test diff --git a/lib/code_corps/github.ex b/lib/code_corps/github.ex index 6f54622a6..1101dc04e 100644 --- a/lib/code_corps/github.ex +++ b/lib/code_corps/github.ex @@ -1,15 +1,41 @@ defmodule CodeCorps.Github do - alias CodeCorps.{User, Repo} + @api Application.get_env(:code_corps, :github_api) + @doc """ - Temporary function until the actual behavior is implemented. + Posts code to github to receive an auth token, associates user with that + auth token. + + Accepts a third parameter, which is a custom API module, for the purposes of + explicit dependency injection during testing. + + Returns one of the following: + + - {:ok, %CodeCorps.User{}} + - {:error, %Ecto.Changeset{}} + - {:error, "some_github_error"} """ - def connect(user, _code), do: {:ok, user} + @spec connect(User.t, String.t, module) :: {:ok, User.t} | {:error, String.t} + def connect(%User{} = user, code, api \\ @api) do + case code |> api.connect do + {:ok, github_auth_token} -> user |> associate(%{github_auth_token: github_auth_token}) + {:error, error} -> {:error, error} + end + end + + @doc """ + Associates user with an auth token + Returns one of the following: + + - {:ok, %CodeCorps.User{}} + - {:error, %Ecto.Changeset{}} + """ + @spec associate(User.t, map) :: {:ok, User.t} | {:error, Ecto.Changeset.t} def associate(user, params) do user - |> User.github_associate_changeset(params) + |> User.github_association_changeset(params) |> Repo.update() end end diff --git a/lib/code_corps/github/api.ex b/lib/code_corps/github/api.ex new file mode 100644 index 000000000..f61107fe3 --- /dev/null +++ b/lib/code_corps/github/api.ex @@ -0,0 +1,44 @@ +defmodule CodeCorps.Github.API do + @moduledoc """ + The boundary module which communicates with the Github API using either + direct requests, or through Tentacat + """ + @behaviour CodeCorps.Github.APIContract + + @client_secret Application.get_env(:code_corps, :github_client_secret) + @client_id Application.get_env(:code_corps, :github_client_id) + + @base_connect_params %{ + client_id: @client_id, + client_secret: @client_secret + } + + @doc """ + Receives a code generated through the client-side github connect process and + posts it to github. + + Returns either an {:ok, access_token}, or an {:error, error_message}. + """ + @spec connect(String.t) :: {:ok, String.t} | {:error, String.t} + def connect(code) do + with {:ok, %HTTPoison.Response{body: response}} <- code |> build_connect_params() |> do_connect(), + {:ok, %{"access_token" => access_token}} <- response |> Poison.decode + do + {:ok, access_token} + else + {:ok, %{"error" => error}} -> {:error, error} + end + end + + @connect_url "https://github.com/login/oauth/access_token" + + @spec do_connect(map) :: {:ok, HTTPoison.Response.t | HTTPoison.AsyncResponse.t} | {:error, HTTPoison.Error.t} + defp do_connect(params) do + HTTPoison.post(@connect_url, "", [{"Accept", "application/json"}], [params: params]) + end + + @spec build_connect_params(String.t) :: map + defp build_connect_params(code) do + @base_connect_params |> Map.put(:code, code) + end +end diff --git a/lib/code_corps/github/api_contract.ex b/lib/code_corps/github/api_contract.ex new file mode 100644 index 000000000..deb1aa4f5 --- /dev/null +++ b/lib/code_corps/github/api_contract.ex @@ -0,0 +1,17 @@ +defmodule CodeCorps.Github.APIContract do + @moduledoc """ + Defines a contract for a github API module, listing all functions the module + should implement. + + This contract should be specified as behaviour for the default `Github.API` + module, as well as any custom module we inject in tests. + """ + + @doc """ + Receives a code string, created in the client part of the github connect process, + returns either an :ok tupple indicating a successful connect process, where + the second element is the auth token string, or an :error tuple, where the + second element is an error message, or a struct + """ + @callback connect(code :: String.t) :: {:ok, auth_token :: String.t} | {:error, error :: String.t} +end diff --git a/lib/code_corps/github/test_api.ex b/lib/code_corps/github/test_api.ex new file mode 100644 index 000000000..d248591be --- /dev/null +++ b/lib/code_corps/github/test_api.ex @@ -0,0 +1,13 @@ +defmodule CodeCorps.Github.TestAPI do + @moduledoc """ + The most basic implementation of an API module for testing. All functions + here should return successes. + + If we want to test the wrapper module, we can specify custom API modules + during function calls. + """ + @behaviour CodeCorps.Github.APIContract + + @spec connect(String.t) :: {:ok, String.t} + def connect(code), do: send(self(), {:ok, code}) +end diff --git a/mix.exs b/mix.exs index 9e20641fe..b09dec7d9 100644 --- a/mix.exs +++ b/mix.exs @@ -45,6 +45,7 @@ defmodule CodeCorps.Mixfile do :segment, :sentry, :stripity_stripe, + :tentacat, :timber, :timex_ecto ] @@ -96,6 +97,7 @@ defmodule CodeCorps.Mixfile do {:sentry, "~> 2.0"}, # Sentry error tracking {:stripity_stripe, git: "https://github.com/code-corps/stripity_stripe.git", branch: "2.0"}, # Stripe {:sweet_xml, "~> 0.5"}, + {:tentacat, "~> 0.5"}, {:timber, "~> 0.4"}, # Logging {:timex, "~> 3.0"}, {:timex_ecto, "~> 3.0"}, diff --git a/mix.lock b/mix.lock index 1f8d63ff0..bc5f94354 100644 --- a/mix.lock +++ b/mix.lock @@ -59,6 +59,7 @@ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []}, "stripity_stripe": {:git, "https://github.com/code-corps/stripity_stripe.git", "1e9b87d73710ef901dd6d735d6f2d0a2cef75d1c", [branch: "2.0"]}, "sweet_xml": {:hex, :sweet_xml, "0.6.5", "dd9cde443212b505d1b5f9758feb2000e66a14d3c449f04c572f3048c66e6697", [:mix], []}, + "tentacat": {:hex, :tentacat, "0.6.2", "e5e5ad95d577dd441e4dcfcab259c9d92b0049f0481a4be6453769d61a956a3b", [:mix], [{:exjsx, "~> 3.2", [hex: :exjsx, optional: false]}, {:httpoison, "~> 0.8", [hex: :httpoison, optional: false]}]}, "timber": {:hex, :timber, "0.4.7", "df3fcd79bcb4eb4b53874d906ef5f3a212937b4bc7b7c5b244745202cc389443", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: true]}, {:phoenix, "~> 1.2", [hex: :phoenix, optional: true]}, {:plug, "~> 1.2", [hex: :plug, optional: true]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]}, "timex": {:hex, :timex, "3.0.8", "71d5ebafcdc557c6c866cdc196c5054f587e7cd1118ad8937e2293d51fc85608", [:mix], [{:combine, "~> 0.7", [hex: :combine, optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, optional: false]}]}, "timex_ecto": {:hex, :timex_ecto, "3.1.1", "37d54f6879d96a6789bb497296531cfb853631de78e152969d95cff03c1368dd", [:mix], [{:ecto, "~> 2.1.0", [hex: :ecto, optional: false]}, {:timex, "~> 3.0", [hex: :timex, optional: false]}]}, diff --git a/priv/repo/migrations/20170526095401_add_github_auth_token_to_users.exs b/priv/repo/migrations/20170526095401_add_github_auth_token_to_users.exs new file mode 100644 index 000000000..51a0a216f --- /dev/null +++ b/priv/repo/migrations/20170526095401_add_github_auth_token_to_users.exs @@ -0,0 +1,9 @@ +defmodule CodeCorps.Repo.Migrations.AddGithubAuthTokenToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add :github_auth_token, :string + end + end +end diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index 68332dfac..9753c566b 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -2,12 +2,16 @@ -- PostgreSQL database dump -- +-- Dumped from database version 9.5.1 +-- Dumped by pg_dump version 9.5.1 + SET statement_timeout = 0; SET lock_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SET check_function_bodies = false; SET client_min_messages = warning; +SET row_security = off; -- -- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: - @@ -47,7 +51,7 @@ SET default_tablespace = ''; SET default_with_oids = false; -- --- Name: auth_token; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: auth_token; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE auth_token ( @@ -79,7 +83,7 @@ ALTER SEQUENCE auth_token_id_seq OWNED BY auth_token.id; -- --- Name: categories; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: categories; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE categories ( @@ -112,7 +116,7 @@ ALTER SEQUENCE categories_id_seq OWNED BY categories.id; -- --- Name: comments; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: comments; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE comments ( @@ -147,7 +151,7 @@ ALTER SEQUENCE comments_id_seq OWNED BY comments.id; -- --- Name: donation_goals; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: donation_goals; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE donation_goals ( @@ -181,7 +185,7 @@ ALTER SEQUENCE donation_goals_id_seq OWNED BY donation_goals.id; -- --- Name: organizations; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: organizations; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE organizations ( @@ -218,7 +222,7 @@ ALTER SEQUENCE organizations_id_seq OWNED BY organizations.id; -- --- Name: previews; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: previews; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE previews ( @@ -251,7 +255,7 @@ ALTER SEQUENCE previews_id_seq OWNED BY previews.id; -- --- Name: project_categories; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: project_categories; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE project_categories ( @@ -283,7 +287,7 @@ ALTER SEQUENCE project_categories_id_seq OWNED BY project_categories.id; -- --- Name: project_skills; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: project_skills; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE project_skills ( @@ -315,7 +319,7 @@ ALTER SEQUENCE project_skills_id_seq OWNED BY project_skills.id; -- --- Name: project_users; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: project_users; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE project_users ( @@ -348,7 +352,7 @@ ALTER SEQUENCE project_users_id_seq OWNED BY project_users.id; -- --- Name: projects; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: projects; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE projects ( @@ -392,7 +396,7 @@ ALTER SEQUENCE projects_id_seq OWNED BY projects.id; -- --- Name: role_skills; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: role_skills; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE role_skills ( @@ -425,7 +429,7 @@ ALTER SEQUENCE role_skills_id_seq OWNED BY role_skills.id; -- --- Name: roles; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: roles; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE roles ( @@ -458,7 +462,7 @@ ALTER SEQUENCE roles_id_seq OWNED BY roles.id; -- --- Name: schema_migrations; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE schema_migrations ( @@ -468,7 +472,7 @@ CREATE TABLE schema_migrations ( -- --- Name: skills; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: skills; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE skills ( @@ -501,7 +505,7 @@ ALTER SEQUENCE skills_id_seq OWNED BY skills.id; -- --- Name: slugged_routes; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: slugged_routes; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE slugged_routes ( @@ -534,7 +538,7 @@ ALTER SEQUENCE slugged_routes_id_seq OWNED BY slugged_routes.id; -- --- Name: stripe_connect_accounts; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_accounts; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_connect_accounts ( @@ -616,7 +620,7 @@ ALTER SEQUENCE stripe_connect_accounts_id_seq OWNED BY stripe_connect_accounts.i -- --- Name: stripe_connect_cards; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_cards; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_connect_cards ( @@ -649,7 +653,7 @@ ALTER SEQUENCE stripe_connect_cards_id_seq OWNED BY stripe_connect_cards.id; -- --- Name: stripe_connect_charges; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_charges; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_connect_charges ( @@ -702,7 +706,7 @@ ALTER SEQUENCE stripe_connect_charges_id_seq OWNED BY stripe_connect_charges.id; -- --- Name: stripe_connect_customers; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_customers; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_connect_customers ( @@ -736,7 +740,7 @@ ALTER SEQUENCE stripe_connect_customers_id_seq OWNED BY stripe_connect_customers -- --- Name: stripe_connect_plans; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_plans; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_connect_plans ( @@ -771,7 +775,7 @@ ALTER SEQUENCE stripe_connect_plans_id_seq OWNED BY stripe_connect_plans.id; -- --- Name: stripe_connect_subscriptions; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_subscriptions; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_connect_subscriptions ( @@ -815,7 +819,7 @@ ALTER SEQUENCE stripe_connect_subscriptions_id_seq OWNED BY stripe_connect_subsc -- --- Name: stripe_events; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_events; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_events ( @@ -853,7 +857,7 @@ ALTER SEQUENCE stripe_events_id_seq OWNED BY stripe_events.id; -- --- Name: stripe_external_accounts; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_external_accounts; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_external_accounts ( @@ -896,7 +900,7 @@ ALTER SEQUENCE stripe_external_accounts_id_seq OWNED BY stripe_external_accounts -- --- Name: stripe_file_upload; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_file_upload; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_file_upload ( @@ -931,7 +935,7 @@ ALTER SEQUENCE stripe_file_upload_id_seq OWNED BY stripe_file_upload.id; -- --- Name: stripe_invoices; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_invoices; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_invoices ( @@ -990,7 +994,7 @@ ALTER SEQUENCE stripe_invoices_id_seq OWNED BY stripe_invoices.id; -- --- Name: stripe_platform_cards; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_platform_cards; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_platform_cards ( @@ -1029,7 +1033,7 @@ ALTER SEQUENCE stripe_platform_cards_id_seq OWNED BY stripe_platform_cards.id; -- --- Name: stripe_platform_customers; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: stripe_platform_customers; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE stripe_platform_customers ( @@ -1065,7 +1069,7 @@ ALTER SEQUENCE stripe_platform_customers_id_seq OWNED BY stripe_platform_custome -- --- Name: task_lists; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: task_lists; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE task_lists ( @@ -1099,7 +1103,7 @@ ALTER SEQUENCE task_lists_id_seq OWNED BY task_lists.id; -- --- Name: task_skills; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: task_skills; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE task_skills ( @@ -1131,7 +1135,7 @@ ALTER SEQUENCE task_skills_id_seq OWNED BY task_skills.id; -- --- Name: tasks; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: tasks; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE tasks ( @@ -1172,7 +1176,7 @@ ALTER SEQUENCE tasks_id_seq OWNED BY tasks.id; -- --- Name: user_categories; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: user_categories; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE user_categories ( @@ -1204,7 +1208,7 @@ ALTER SEQUENCE user_categories_id_seq OWNED BY user_categories.id; -- --- Name: user_roles; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: user_roles; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE user_roles ( @@ -1236,7 +1240,7 @@ ALTER SEQUENCE user_roles_id_seq OWNED BY user_roles.id; -- --- Name: user_skills; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: user_skills; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE user_skills ( @@ -1268,7 +1272,7 @@ ALTER SEQUENCE user_skills_id_seq OWNED BY user_skills.id; -- --- Name: user_tasks; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: user_tasks; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE user_tasks ( @@ -1300,7 +1304,7 @@ ALTER SEQUENCE user_tasks_id_seq OWNED BY user_tasks.id; -- --- Name: users; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: users; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE users ( @@ -1320,7 +1324,8 @@ CREATE TABLE users ( cloudinary_public_id character varying(255), default_color character varying(255), sign_up_context character varying(255) DEFAULT 'default'::character varying, - github_id character varying(255) + github_id character varying(255), + github_auth_token character varying(255) ); @@ -1582,7 +1587,7 @@ ALTER TABLE ONLY users ALTER COLUMN id SET DEFAULT nextval('users_id_seq'::regcl -- --- Name: auth_token_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: auth_token_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY auth_token @@ -1590,7 +1595,7 @@ ALTER TABLE ONLY auth_token -- --- Name: categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY categories @@ -1598,7 +1603,7 @@ ALTER TABLE ONLY categories -- --- Name: comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: comments_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY comments @@ -1606,7 +1611,7 @@ ALTER TABLE ONLY comments -- --- Name: donation_goals_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: donation_goals_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY donation_goals @@ -1614,7 +1619,7 @@ ALTER TABLE ONLY donation_goals -- --- Name: organizations_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: organizations_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY organizations @@ -1622,7 +1627,7 @@ ALTER TABLE ONLY organizations -- --- Name: previews_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: previews_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY previews @@ -1630,7 +1635,7 @@ ALTER TABLE ONLY previews -- --- Name: project_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: project_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY project_categories @@ -1638,7 +1643,7 @@ ALTER TABLE ONLY project_categories -- --- Name: project_skills_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: project_skills_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY project_skills @@ -1646,7 +1651,7 @@ ALTER TABLE ONLY project_skills -- --- Name: project_users_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: project_users_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY project_users @@ -1654,7 +1659,7 @@ ALTER TABLE ONLY project_users -- --- Name: projects_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: projects_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY projects @@ -1662,7 +1667,7 @@ ALTER TABLE ONLY projects -- --- Name: role_skills_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: role_skills_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY role_skills @@ -1670,7 +1675,7 @@ ALTER TABLE ONLY role_skills -- --- Name: roles_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: roles_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY roles @@ -1678,7 +1683,7 @@ ALTER TABLE ONLY roles -- --- Name: schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY schema_migrations @@ -1686,7 +1691,7 @@ ALTER TABLE ONLY schema_migrations -- --- Name: skills_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: skills_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY skills @@ -1694,7 +1699,7 @@ ALTER TABLE ONLY skills -- --- Name: slugged_routes_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: slugged_routes_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY slugged_routes @@ -1702,7 +1707,7 @@ ALTER TABLE ONLY slugged_routes -- --- Name: stripe_connect_cards_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_cards_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY stripe_connect_cards @@ -1710,7 +1715,7 @@ ALTER TABLE ONLY stripe_connect_cards -- --- Name: stripe_connect_charges_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_charges_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY stripe_connect_charges @@ -1718,7 +1723,7 @@ ALTER TABLE ONLY stripe_connect_charges -- --- Name: stripe_connect_customers_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_customers_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY stripe_connect_customers @@ -1726,7 +1731,7 @@ ALTER TABLE ONLY stripe_connect_customers -- --- Name: stripe_events_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: stripe_events_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY stripe_events @@ -1734,7 +1739,7 @@ ALTER TABLE ONLY stripe_events -- --- Name: stripe_external_accounts_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: stripe_external_accounts_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY stripe_external_accounts @@ -1742,7 +1747,7 @@ ALTER TABLE ONLY stripe_external_accounts -- --- Name: stripe_file_upload_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: stripe_file_upload_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY stripe_file_upload @@ -1750,7 +1755,7 @@ ALTER TABLE ONLY stripe_file_upload -- --- Name: stripe_invoices_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: stripe_invoices_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY stripe_invoices @@ -1758,7 +1763,7 @@ ALTER TABLE ONLY stripe_invoices -- --- Name: task_lists_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: task_lists_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY task_lists @@ -1766,7 +1771,7 @@ ALTER TABLE ONLY task_lists -- --- Name: task_skills_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: task_skills_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY task_skills @@ -1774,7 +1779,7 @@ ALTER TABLE ONLY task_skills -- --- Name: user_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: user_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY user_categories @@ -1782,7 +1787,7 @@ ALTER TABLE ONLY user_categories -- --- Name: user_roles_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: user_roles_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY user_roles @@ -1790,7 +1795,7 @@ ALTER TABLE ONLY user_roles -- --- Name: user_skills_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: user_skills_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY user_skills @@ -1798,7 +1803,7 @@ ALTER TABLE ONLY user_skills -- --- Name: user_tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: user_tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY user_tasks @@ -1806,7 +1811,7 @@ ALTER TABLE ONLY user_tasks -- --- Name: users_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: users_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY users @@ -1814,378 +1819,378 @@ ALTER TABLE ONLY users -- --- Name: auth_token_user_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: auth_token_user_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX auth_token_user_id_index ON auth_token USING btree (user_id); -- --- Name: comments_task_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: comments_task_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX comments_task_id_index ON comments USING btree (task_id); -- --- Name: comments_user_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: comments_user_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX comments_user_id_index ON comments USING btree (user_id); -- --- Name: donation_goals_current_unique_to_project; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: donation_goals_current_unique_to_project; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX donation_goals_current_unique_to_project ON donation_goals USING btree (project_id) WHERE current; -- --- Name: donation_goals_project_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: donation_goals_project_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX donation_goals_project_id_index ON donation_goals USING btree (project_id); -- --- Name: index_categories_on_slug; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: index_categories_on_slug; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX index_categories_on_slug ON categories USING btree (lower((slug)::text)); -- --- Name: index_projects_on_role_id_skill_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: index_projects_on_role_id_skill_id; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX index_projects_on_role_id_skill_id ON role_skills USING btree (role_id, skill_id); -- --- Name: index_projects_on_slug; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: index_projects_on_slug; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX index_projects_on_slug ON projects USING btree (lower((slug)::text)); -- --- Name: index_projects_on_user_id_skill_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: index_projects_on_user_id_skill_id; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX index_projects_on_user_id_skill_id ON user_skills USING btree (user_id, skill_id); -- --- Name: index_skills_on_title; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: index_skills_on_title; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX index_skills_on_title ON skills USING btree (lower((title)::text)); -- --- Name: organizations_approved_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: organizations_approved_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX organizations_approved_index ON organizations USING btree (approved); -- --- Name: organizations_lower_slug_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: organizations_lower_slug_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX organizations_lower_slug_index ON organizations USING btree (lower((slug)::text)); -- --- Name: project_categories_project_id_category_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: project_categories_project_id_category_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX project_categories_project_id_category_id_index ON project_categories USING btree (project_id, category_id); -- --- Name: project_skills_project_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: project_skills_project_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX project_skills_project_id_index ON project_skills USING btree (project_id); -- --- Name: project_skills_project_id_skill_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: project_skills_project_id_skill_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX project_skills_project_id_skill_id_index ON project_skills USING btree (project_id, skill_id); -- --- Name: project_skills_skill_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: project_skills_skill_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX project_skills_skill_id_index ON project_skills USING btree (skill_id); -- --- Name: project_users_user_id_project_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: project_users_user_id_project_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX project_users_user_id_project_id_index ON project_users USING btree (user_id, project_id); -- --- Name: projects_approved_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: projects_approved_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX projects_approved_index ON projects USING btree (approved); -- --- Name: slugged_routes_lower_slug_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: slugged_routes_lower_slug_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX slugged_routes_lower_slug_index ON slugged_routes USING btree (lower((slug)::text)); -- --- Name: stripe_connect_accounts_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_accounts_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_accounts_id_from_stripe_index ON stripe_connect_accounts USING btree (id_from_stripe); -- --- Name: stripe_connect_accounts_organization_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_accounts_organization_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_accounts_organization_id_index ON stripe_connect_accounts USING btree (organization_id); -- --- Name: stripe_connect_accounts_pkey; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_accounts_pkey; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_accounts_pkey ON stripe_connect_accounts USING btree (id); -- --- Name: stripe_connect_cards_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_cards_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_cards_id_from_stripe_index ON stripe_connect_cards USING btree (id_from_stripe); -- --- Name: stripe_connect_cards_stripe_connect_account_id_stripe_platform_; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_cards_stripe_connect_account_id_stripe_platform_; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_cards_stripe_connect_account_id_stripe_platform_ ON stripe_connect_cards USING btree (stripe_connect_account_id, stripe_platform_card_id); -- --- Name: stripe_connect_charges_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_charges_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_charges_id_from_stripe_index ON stripe_connect_charges USING btree (id_from_stripe); -- --- Name: stripe_connect_customers_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_customers_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_customers_id_from_stripe_index ON stripe_connect_customers USING btree (id_from_stripe); -- --- Name: stripe_connect_customers_stripe_connect_account_id_stripe_platf; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_customers_stripe_connect_account_id_stripe_platf; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_customers_stripe_connect_account_id_stripe_platf ON stripe_connect_customers USING btree (stripe_connect_account_id, stripe_platform_customer_id); -- --- Name: stripe_connect_plans_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_plans_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_plans_id_from_stripe_index ON stripe_connect_plans USING btree (id_from_stripe); -- --- Name: stripe_connect_plans_pkey; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_plans_pkey; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_plans_pkey ON stripe_connect_plans USING btree (id); -- --- Name: stripe_connect_plans_project_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_plans_project_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_plans_project_id_index ON stripe_connect_plans USING btree (project_id); -- --- Name: stripe_connect_subscriptions_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_subscriptions_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_subscriptions_id_from_stripe_index ON stripe_connect_subscriptions USING btree (id_from_stripe); -- --- Name: stripe_connect_subscriptions_pkey; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_subscriptions_pkey; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_connect_subscriptions_pkey ON stripe_connect_subscriptions USING btree (id); -- --- Name: stripe_connect_subscriptions_plan_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_subscriptions_plan_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX stripe_connect_subscriptions_plan_id_from_stripe_index ON stripe_connect_subscriptions USING btree (plan_id_from_stripe); -- --- Name: stripe_connect_subscriptions_stripe_connect_plan_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_subscriptions_stripe_connect_plan_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX stripe_connect_subscriptions_stripe_connect_plan_id_index ON stripe_connect_subscriptions USING btree (stripe_connect_plan_id); -- --- Name: stripe_connect_subscriptions_user_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_connect_subscriptions_user_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX stripe_connect_subscriptions_user_id_index ON stripe_connect_subscriptions USING btree (user_id); -- --- Name: stripe_events_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_events_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_events_id_from_stripe_index ON stripe_events USING btree (id_from_stripe); -- --- Name: stripe_invoices_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_invoices_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_invoices_id_from_stripe_index ON stripe_invoices USING btree (id_from_stripe); -- --- Name: stripe_platform_cards_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_platform_cards_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_platform_cards_id_from_stripe_index ON stripe_platform_cards USING btree (id_from_stripe); -- --- Name: stripe_platform_cards_pkey; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_platform_cards_pkey; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_platform_cards_pkey ON stripe_platform_cards USING btree (id); -- --- Name: stripe_platform_cards_user_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_platform_cards_user_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_platform_cards_user_id_index ON stripe_platform_cards USING btree (user_id); -- --- Name: stripe_platform_customers_id_from_stripe_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_platform_customers_id_from_stripe_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_platform_customers_id_from_stripe_index ON stripe_platform_customers USING btree (id_from_stripe); -- --- Name: stripe_platform_customers_pkey; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_platform_customers_pkey; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_platform_customers_pkey ON stripe_platform_customers USING btree (id); -- --- Name: stripe_platform_customers_user_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: stripe_platform_customers_user_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX stripe_platform_customers_user_id_index ON stripe_platform_customers USING btree (user_id); -- --- Name: task_lists_project_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: task_lists_project_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX task_lists_project_id_index ON task_lists USING btree (project_id); -- --- Name: task_skills_task_id_skill_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: task_skills_task_id_skill_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX task_skills_task_id_skill_id_index ON task_skills USING btree (task_id, skill_id); -- --- Name: tasks_number_project_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: tasks_number_project_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX tasks_number_project_id_index ON tasks USING btree (number, project_id); -- --- Name: tasks_pkey; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: tasks_pkey; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX tasks_pkey ON tasks USING btree (id); -- --- Name: tasks_project_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: tasks_project_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX tasks_project_id_index ON tasks USING btree (project_id); -- --- Name: tasks_user_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: tasks_user_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX tasks_user_id_index ON tasks USING btree (user_id); -- --- Name: user_categories_user_id_category_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: user_categories_user_id_category_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX user_categories_user_id_category_id_index ON user_categories USING btree (user_id, category_id); -- --- Name: user_roles_user_id_role_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: user_roles_user_id_role_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX user_roles_user_id_role_id_index ON user_roles USING btree (user_id, role_id); -- --- Name: user_tasks_user_id_task_id_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: user_tasks_user_id_task_id_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX user_tasks_user_id_task_id_index ON user_tasks USING btree (user_id, task_id); -- --- Name: users_email_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: users_email_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX users_email_index ON users USING btree (email); -- --- Name: users_lower_username_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- Name: users_lower_username_index; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX users_lower_username_index ON users USING btree (lower((username)::text)); @@ -2594,5 +2599,5 @@ ALTER TABLE ONLY user_tasks -- PostgreSQL database dump complete -- -INSERT INTO "schema_migrations" (version) VALUES (20160723215749), (20160804000000), (20160804001111), (20160805132301), (20160805203929), (20160808143454), (20160809214736), (20160810124357), (20160815125009), (20160815143002), (20160816020347), (20160816034021), (20160817220118), (20160818000944), (20160818132546), (20160820113856), (20160820164905), (20160822002438), (20160822004056), (20160822011624), (20160822020401), (20160822044612), (20160830081224), (20160830224802), (20160911233738), (20160912002705), (20160912145957), (20160918003206), (20160928232404), (20161003185918), (20161019090945), (20161019110737), (20161020144622), (20161021131026), (20161031001615), (20161121005339), (20161121014050), (20161121043941), (20161121045709), (20161122015942), (20161123081114), (20161123150943), (20161124085742), (20161125200620), (20161126045705), (20161127054559), (20161205024856), (20161207112519), (20161209192504), (20161212005641), (20161214005935), (20161215052051), (20161216051447), (20161218005913), (20161219160401), (20161219163909), (20161220141753), (20161221085759), (20161226213600), (20161231063614), (20170102130055), (20170102181053), (20170104113708), (20170104212623), (20170104235423), (20170106013143), (20170115035159), (20170115230549), (20170121014100), (20170131234029), (20170201014901), (20170201025454), (20170201035458), (20170201183258), (20170220032224), (20170224233516), (20170226050552), (20170228085250), (20170308214128), (20170308220713), (20170308222552), (20170313130611), (20170318032449), (20170318082740), (20170324194827), (20170424215355), (20170501225441); +INSERT INTO "schema_migrations" (version) VALUES (20160723215749), (20160804000000), (20160804001111), (20160805132301), (20160805203929), (20160808143454), (20160809214736), (20160810124357), (20160815125009), (20160815143002), (20160816020347), (20160816034021), (20160817220118), (20160818000944), (20160818132546), (20160820113856), (20160820164905), (20160822002438), (20160822004056), (20160822011624), (20160822020401), (20160822044612), (20160830081224), (20160830224802), (20160911233738), (20160912002705), (20160912145957), (20160918003206), (20160928232404), (20161003185918), (20161019090945), (20161019110737), (20161020144622), (20161021131026), (20161031001615), (20161121005339), (20161121014050), (20161121043941), (20161121045709), (20161122015942), (20161123081114), (20161123150943), (20161124085742), (20161125200620), (20161126045705), (20161127054559), (20161205024856), (20161207112519), (20161209192504), (20161212005641), (20161214005935), (20161215052051), (20161216051447), (20161218005913), (20161219160401), (20161219163909), (20161220141753), (20161221085759), (20161226213600), (20161231063614), (20170102130055), (20170102181053), (20170104113708), (20170104212623), (20170104235423), (20170106013143), (20170115035159), (20170115230549), (20170121014100), (20170131234029), (20170201014901), (20170201025454), (20170201035458), (20170201183258), (20170220032224), (20170224233516), (20170226050552), (20170228085250), (20170308214128), (20170308220713), (20170308222552), (20170313130611), (20170318032449), (20170318082740), (20170324194827), (20170424215355), (20170501225441), (20170526095401); diff --git a/test/controllers/user_controller_test.exs b/test/controllers/user_controller_test.exs index 483baf943..9baed8590 100644 --- a/test/controllers/user_controller_test.exs +++ b/test/controllers/user_controller_test.exs @@ -246,7 +246,7 @@ defmodule CodeCorps.UserControllerTest do test "return the user when current user connects successfully", %{conn: conn} do user = insert(:user) - code = %{"code" => "client generated code"} + code = %{"code" => "valid_code"} path = user_path(conn, :github_connect, code) diff --git a/test/lib/code_corps/github_test.exs b/test/lib/code_corps/github_test.exs index a1b5c31b4..c8a1a8657 100644 --- a/test/lib/code_corps/github_test.exs +++ b/test/lib/code_corps/github_test.exs @@ -1,20 +1,52 @@ defmodule CodeCorps.GithubTest do use CodeCorps.ModelCase - alias CodeCorps.Github + + alias CodeCorps.{ + Github, User + } + + alias Ecto.Changeset describe "associate/2" do - test "should update the user with the github_id" do + test "updates the user, returns :ok tuple with user" do user = insert(:user) - params = %{github_id: "foobar"} - {:ok, result} = Github.associate(user, params) - assert result.github_id == "foobar" + params = %{github_auth_token: "foobar"} + {:ok, %User{} = returned_user} = Github.associate(user, params) + assert user.id == returned_user.id + assert returned_user.github_auth_token == "foobar" end - test "should return the error with a changeset" do + test "returns :error tupple with changeset if there are validation errors" do user = insert(:user) params = %{} - {:error, changeset} = Github.associate(user, params) + {:error, %Changeset{} = changeset} = Github.associate(user, params) refute changeset.valid? end end + + defmodule SuccessAPI do + @behaviour CodeCorps.Github.APIContract + def connect(_code), do: {:ok, "foo_auth_token"} + end + + defmodule ErrorAPI do + @behaviour CodeCorps.Github.APIContract + def connect(_code), do: {:error, "foo_error"} + end + + describe "connect/2" do + test "posts to github, updates user if reply is ok, returns updated user" do + user = insert(:user) + + {:ok, %User{} = returned_user} = Github.connect(user, "foo", SuccessAPI) + + assert returned_user.id == user.id + assert returned_user.github_auth_token == "foo_auth_token" + end + + test "posts to github, returns error if reply is not ok" do + user = insert(:user) + assert {:error, "foo_error"} == Github.connect(user, "foo", ErrorAPI) + end + end end diff --git a/test/models/user_test.exs b/test/models/user_test.exs index 23a63eb16..a8fbfdd81 100644 --- a/test/models/user_test.exs +++ b/test/models/user_test.exs @@ -140,16 +140,16 @@ defmodule CodeCorps.UserTest do end end - describe "github_associate_changeset" do - test "should cast github_id" do + describe "github_association_changeset" do + test "should cast github_auth_token" do user = insert(:user) - changeset = user |> User.github_associate_changeset(%{github_id: "foobar"}) + changeset = user |> User.github_association_changeset(%{github_auth_token: "foobar"}) assert changeset.valid? - assert changeset.changes.github_id == "foobar" + assert changeset.changes.github_auth_token == "foobar" end - test "github_id should be required" do + test "github_auth_token should be required" do user = insert(:user) - changeset = user |> User.github_associate_changeset(%{}) + changeset = user |> User.github_association_changeset(%{}) refute changeset.valid? end end diff --git a/web/controllers/user_controller.ex b/web/controllers/user_controller.ex index 038b4c465..6cb7d769a 100644 --- a/web/controllers/user_controller.ex +++ b/web/controllers/user_controller.ex @@ -35,7 +35,7 @@ defmodule CodeCorps.UserController do end end - def github_connect(conn, code) do + def github_connect(conn, %{"code" => code}) do current_user = Guardian.Plug.current_resource(conn) with {:ok, user} <- Github.connect(current_user, code) do diff --git a/web/models/project.ex b/web/models/project.ex index a57532099..bdf63c6dd 100644 --- a/web/models/project.ex +++ b/web/models/project.ex @@ -13,6 +13,8 @@ defmodule CodeCorps.Project do alias CodeCorps.Services.MarkdownRendererService alias CodeCorps.TaskList + alias Ecto.Changeset + @type t :: %__MODULE__{} schema "projects" do diff --git a/web/models/user.ex b/web/models/user.ex index cd9ec0797..969ef4e55 100644 --- a/web/models/user.ex +++ b/web/models/user.ex @@ -30,10 +30,12 @@ defmodule CodeCorps.User do field :twitter, :string field :username, :string field :website, :string - field :github_id, :string field :state, :string, default: "signed_up" field :state_transition, :string, virtual: true + field :github_id, :string + field :github_auth_token, :string + has_one :slugged_route, SluggedRoute has_many :project_users, CodeCorps.ProjectUser @@ -95,10 +97,10 @@ defmodule CodeCorps.User do |> apply_state_transition(struct) end - def github_associate_changeset(struct, params) do + def github_association_changeset(struct, params) do struct - |> cast(params, [:github_id]) - |> validate_required([:github_id]) + |> cast(params, [:github_auth_token]) + |> validate_required([:github_auth_token]) end def reset_password_changeset(struct, params) do