diff --git a/lib/code_corps/accounts/accounts.ex b/lib/code_corps/accounts/accounts.ex index b9fdd7a79..dffa67713 100644 --- a/lib/code_corps/accounts/accounts.ex +++ b/lib/code_corps/accounts/accounts.ex @@ -18,9 +18,19 @@ defmodule CodeCorps.Accounts do @spec create_from_github(map) :: {:ok, User.t} | {:error, Changeset.t} def create_from_github(%{} = attrs) do %User{} - |> Changeset.change(attrs |> Adapters.User.from_github_user()) + |> create_from_github_changeset(attrs) + |> Repo.insert + end + + @doc ~S""" + Casts a changeset used for creating a user account from a github user payload + """ + @spec create_from_github_changeset(struct, map) :: Changeset.t + def create_from_github_changeset(struct, %{} = params) do + struct + |> Changeset.change(params |> Adapters.User.from_github_user()) |> Changeset.put_change(:context, "github") |> Changeset.unique_constraint(:email) - |> Repo.insert + |> Changeset.validate_inclusion(:type, ["bot", "user"]) end end diff --git a/lib/code_corps/github/adapters/user.ex b/lib/code_corps/github/adapters/user.ex index ced6eb9e5..ca2bb6fa0 100644 --- a/lib/code_corps/github/adapters/user.ex +++ b/lib/code_corps/github/adapters/user.ex @@ -8,15 +8,18 @@ defmodule CodeCorps.GitHub.Adapters.User do {:github_avatar_url, ["avatar_url"]}, {:github_id, ["id"]}, {:github_username, ["login"]}, - {:email, ["email"]} + {:email, ["email"]}, + {:type, ["type"]} ] @doc ~S""" Converts a Github user payload into a map of attributes suitable for creating or updating a `CodeCorps.User` - Any nil values are removed here, since we do not want to, for example, delete - an existing email just because the github payload doesn't have that data. + Any `nil` values are removed here. For example, we don't want to delete + an existing email just because the GitHub payload is missing that data. + + The `type` gets transformed to match our expected values for user type. """ @spec from_github_user(map) :: map def from_github_user(%{} = payload) do @@ -24,5 +27,11 @@ defmodule CodeCorps.GitHub.Adapters.User do |> CodeCorps.Adapter.MapTransformer.transform(@mapping) |> Enum.reject(fn {_, v} -> is_nil(v) end) |> Map.new + |> transform_type end + + @spec transform_type(map) :: map + defp transform_type(%{:type => "Bot"} = map), do: Map.put(map, :type, "bot") + defp transform_type(%{:type => "User"} = map), do: Map.put(map, :type, "user") + defp transform_type(map), do: map end diff --git a/lib/code_corps/github/event/issue_comment/user_linker.ex b/lib/code_corps/github/event/issue_comment/user_linker.ex index 5dfd04a42..8249a2d4a 100644 --- a/lib/code_corps/github/event/issue_comment/user_linker.ex +++ b/lib/code_corps/github/event/issue_comment/user_linker.ex @@ -1,29 +1,66 @@ defmodule CodeCorps.GitHub.Event.IssueComment.UserLinker do @moduledoc ~S""" - In charge of finding a user to link with a Task when processing an + In charge of finding a user to link with a Comment when processing an IssueComment webhook. """ + import Ecto.Query + alias CodeCorps.{ Accounts, + Comment, Repo, User } + @typep linking_result :: {:ok, User.t} | + {:error, Ecto.Changeset.t} + {:error, :multiple_users} + @doc ~S""" - Finds or creates a user using information contained in an Issues webhook - payload + Finds or creates a user using information contained in a GitHub IssueComment + webhook payload. + + The process is as follows: + - Find all affected comments and extract their user data. + - Search for the user in our database. + - If we match a single user, then the comment should be for that user. + - If there are no matching users, then the comment was created on Github by + someone who does not have a matching GitHub-connected Code Corps account. + We create a placeholder user account until that GitHub user is claimed by + a Code Corps user. + created. + - If there are multiple matching users, this is an unexpected scenario and + should error out. """ @spec find_or_create_user(map) :: {:ok, User.t} - def find_or_create_user(%{"comment" => %{"user" => user_attrs}}) do - case user_attrs |> find_user() do - nil -> user_attrs |> Accounts.create_from_github - %User{} = user -> {:ok, user} - end + def find_or_create_user(%{"comment" => %{"user" => user_attrs}} = attrs) do + attrs + |> match_users + |> marshall_response(user_attrs) + end + + @spec match_users(map) :: list(User.t) + defp match_users(%{"comment" => %{"id" => github_id}}) do + query = from u in User, + distinct: u.id, + join: c in Comment, on: u.id == c.user_id, where: c.github_id == ^github_id + + query |> Repo.all + end + + @spec marshall_response(list, map) :: linking_result + defp marshall_response([%User{} = single_user], %{}), do: {:ok, single_user} + defp marshall_response([], %{} = user_attrs) do + user_attrs |> find_or_create_disassociated_user() end + defp marshall_response([_head | _tail], %{}), do: {:error, :multiple_users} - @spec find_user(map) :: User.t | nil - defp find_user(%{"id" => github_id}) do - User |> Repo.get_by(github_id: github_id) + @spec find_or_create_disassociated_user(map) :: {:ok, User.t} + def find_or_create_disassociated_user(%{"id" => github_id} = attrs) do + case User |> Repo.get_by(github_id: github_id) do + nil -> attrs |> Accounts.create_from_github + %User{} = user -> {:ok, user} + end end end diff --git a/lib/code_corps/github/event/issues/user_linker.ex b/lib/code_corps/github/event/issues/user_linker.ex index 56384af1b..56681742e 100644 --- a/lib/code_corps/github/event/issues/user_linker.ex +++ b/lib/code_corps/github/event/issues/user_linker.ex @@ -2,28 +2,73 @@ defmodule CodeCorps.GitHub.Event.Issues.UserLinker do @moduledoc ~S""" In charge of finding a user to link with a Task when processing an Issues webhook. + + The only entry point is `find_or_create_user/1`. """ + import Ecto.Query + alias CodeCorps.{ Accounts, + GithubRepo, Repo, + Task, User } + @typep linking_result :: {:ok, User.t} | + {:error, Ecto.Changeset.t} + {:error, :multiple_users} + @doc ~S""" - Finds or creates a user using information contained in an Issues webhook - payload + Finds or creates a user using information contained in a GitHub Issue + webhook payload. + + The process is as follows: + - Find all affected tasks and extract their user data. + - Search for the user in our database. + - If we match a single user, then the issue should be related to that user. + - If there are no matching users, then the issue was created on Github by + someone who does not have a matching GitHub-connected Code Corps account. + We create a placeholder user account until that GitHub user is claimed by + a Code Corps user. + created. + - If there are multiple matching users, this is an unexpected scenario and + should error out. """ - @spec find_or_create_user(map) :: {:ok, User.t} - def find_or_create_user(%{"issue" => %{"user" => user_attrs}}) do - case user_attrs |> find_user() do - nil -> user_attrs |> Accounts.create_from_github - %User{} = user -> {:ok, user} - end + @spec find_or_create_user(map) :: linking_result + def find_or_create_user(%{"issue" => %{"user" => user_attrs}} = attrs) do + attrs + |> match_users + |> marshall_response(user_attrs) + end + + @spec match_users(map) :: list(User.t) + defp match_users( + %{ + "issue" => %{"number" => github_issue_number}, + "repository" =>%{"id" => github_repo_id}}) do + + query = from u in User, + distinct: u.id, + join: t in Task, on: u.id == t.user_id, where: t.github_issue_number == ^github_issue_number, + join: r in GithubRepo, on: r.id == t.github_repo_id, where: r.github_id == ^github_repo_id + + query |> Repo.all end - @spec find_user(map) :: User.t | nil - defp find_user(%{"id" => github_id}) do - User |> Repo.get_by(github_id: github_id) + @spec marshall_response(list, map) :: linking_result + defp marshall_response([%User{} = single_user], %{}), do: {:ok, single_user} + defp marshall_response([], %{} = user_attrs) do + user_attrs |> find_or_create_disassociated_user() + end + defp marshall_response([_head | _tail], %{}), do: {:error, :multiple_users} + + @spec find_or_create_disassociated_user(map) :: {:ok, User.t} + def find_or_create_disassociated_user(%{"id" => github_id} = attrs) do + case User |> Repo.get_by(github_id: github_id) do + nil -> attrs |> Accounts.create_from_github + %User{} = user -> {:ok, user} + end end end diff --git a/lib/code_corps/model/user.ex b/lib/code_corps/model/user.ex index e86e6be6c..47422af00 100644 --- a/lib/code_corps/model/user.ex +++ b/lib/code_corps/model/user.ex @@ -33,6 +33,7 @@ defmodule CodeCorps.User do field :password_confirmation, :string, virtual: true field :sign_up_context, :string, default: "default" field :twitter, :string + field :type, :string, default: "user" field :username, :string field :website, :string field :state, :string, default: "signed_up" @@ -88,6 +89,7 @@ defmodule CodeCorps.User do |> validate_slug(:username) |> unique_constraint(:username, name: :users_lower_username_index) |> unique_constraint(:email) + |> put_change(:type, "user") |> put_pass_hash() |> put_slugged_route() |> generate_icon_color(:default_color) diff --git a/priv/repo/migrations/20170925214512_add_type_to_users.exs b/priv/repo/migrations/20170925214512_add_type_to_users.exs new file mode 100644 index 000000000..51fbe2169 --- /dev/null +++ b/priv/repo/migrations/20170925214512_add_type_to_users.exs @@ -0,0 +1,9 @@ +defmodule CodeCorps.Repo.Migrations.AddTypeToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add :type, :string, default: "user" + end + end +end diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index 5f95555a0..47d0e41dc 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -2,8 +2,8 @@ -- PostgreSQL database dump -- --- Dumped from database version 9.5.4 --- Dumped by pg_dump version 9.5.4 +-- Dumped from database version 9.5.1 +-- Dumped by pg_dump version 9.5.1 SET statement_timeout = 0; SET lock_timeout = 0; @@ -55,9 +55,9 @@ SET default_with_oids = false; -- CREATE TABLE auth_token ( - id integer NOT NULL, + id bigint NOT NULL, value character varying(255), - user_id integer, + user_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -87,7 +87,7 @@ ALTER SEQUENCE auth_token_id_seq OWNED BY auth_token.id; -- CREATE TABLE categories ( - id integer NOT NULL, + id bigint NOT NULL, name character varying(255) NOT NULL, slug character varying(255) NOT NULL, description text, @@ -120,11 +120,11 @@ ALTER SEQUENCE categories_id_seq OWNED BY categories.id; -- CREATE TABLE comments ( - id integer NOT NULL, + id bigint NOT NULL, body text NOT NULL, markdown text, - user_id integer NOT NULL, - task_id integer NOT NULL, + user_id bigint NOT NULL, + task_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, github_id integer @@ -155,10 +155,10 @@ ALTER SEQUENCE comments_id_seq OWNED BY comments.id; -- CREATE TABLE donation_goals ( - id integer NOT NULL, + id bigint NOT NULL, amount integer, description text, - project_id integer, + project_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, current boolean DEFAULT false @@ -189,12 +189,12 @@ ALTER SEQUENCE donation_goals_id_seq OWNED BY donation_goals.id; -- CREATE TABLE github_app_installations ( - id integer NOT NULL, + id bigint NOT NULL, github_id integer, installed boolean DEFAULT true, state character varying(255), - project_id integer, - user_id integer, + project_id bigint, + user_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, access_token character varying(255), @@ -232,7 +232,7 @@ ALTER SEQUENCE github_app_installations_id_seq OWNED BY github_app_installations -- CREATE TABLE github_events ( - id integer NOT NULL, + id bigint NOT NULL, action character varying(255), github_delivery_id character varying(255), status character varying(255), @@ -267,14 +267,14 @@ ALTER SEQUENCE github_events_id_seq OWNED BY github_events.id; -- CREATE TABLE github_repos ( - id integer NOT NULL, + id bigint NOT NULL, github_id integer, name character varying(255), github_account_id integer, github_account_login character varying(255), github_account_avatar_url character varying(255), github_account_type character varying(255), - github_app_installation_id integer, + github_app_installation_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -304,9 +304,9 @@ ALTER SEQUENCE github_repos_id_seq OWNED BY github_repos.id; -- CREATE TABLE organization_github_app_installations ( - id integer NOT NULL, - organization_id integer, - github_app_installation_id integer, + id bigint NOT NULL, + organization_id bigint, + github_app_installation_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -336,7 +336,7 @@ ALTER SEQUENCE organization_github_app_installations_id_seq OWNED BY organizatio -- CREATE TABLE organization_invites ( - id integer NOT NULL, + id bigint NOT NULL, code character varying(255) NOT NULL, email character varying(255) NOT NULL, title character varying(255) NOT NULL, @@ -370,7 +370,7 @@ ALTER SEQUENCE organization_invites_id_seq OWNED BY organization_invites.id; -- CREATE TABLE organizations ( - id integer NOT NULL, + id bigint NOT NULL, name text NOT NULL, description text NOT NULL, slug character varying(255) NOT NULL, @@ -379,7 +379,7 @@ CREATE TABLE organizations ( approved boolean DEFAULT false, cloudinary_public_id character varying(255), default_color character varying(255), - owner_id integer + owner_id bigint ); @@ -407,10 +407,10 @@ ALTER SEQUENCE organizations_id_seq OWNED BY organizations.id; -- CREATE TABLE previews ( - id integer NOT NULL, + id bigint NOT NULL, markdown text NOT NULL, body text NOT NULL, - user_id integer NOT NULL, + user_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -440,9 +440,9 @@ ALTER SEQUENCE previews_id_seq OWNED BY previews.id; -- CREATE TABLE project_categories ( - id integer NOT NULL, - project_id integer NOT NULL, - category_id integer NOT NULL, + id bigint NOT NULL, + project_id bigint NOT NULL, + category_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -472,9 +472,9 @@ ALTER SEQUENCE project_categories_id_seq OWNED BY project_categories.id; -- CREATE TABLE project_github_repos ( - id integer NOT NULL, - project_id integer, - github_repo_id integer, + id bigint NOT NULL, + project_id bigint, + github_repo_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -504,9 +504,9 @@ ALTER SEQUENCE project_github_repos_id_seq OWNED BY project_github_repos.id; -- CREATE TABLE project_skills ( - id integer NOT NULL, - project_id integer NOT NULL, - skill_id integer NOT NULL, + id bigint NOT NULL, + project_id bigint NOT NULL, + skill_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -536,10 +536,10 @@ ALTER SEQUENCE project_skills_id_seq OWNED BY project_skills.id; -- CREATE TABLE project_users ( - id integer NOT NULL, + id bigint NOT NULL, role character varying(255) NOT NULL, - project_id integer NOT NULL, - user_id integer NOT NULL, + project_id bigint NOT NULL, + user_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -569,13 +569,13 @@ ALTER SEQUENCE project_users_id_seq OWNED BY project_users.id; -- CREATE TABLE projects ( - id integer NOT NULL, + id bigint NOT NULL, description text, long_description_body text, long_description_markdown text, slug character varying(255) NOT NULL, title character varying(255) NOT NULL, - organization_id integer NOT NULL, + organization_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, total_monthly_donated integer DEFAULT 0, @@ -614,9 +614,9 @@ ALTER SEQUENCE projects_id_seq OWNED BY projects.id; -- CREATE TABLE role_skills ( - id integer NOT NULL, - role_id integer NOT NULL, - skill_id integer NOT NULL, + id bigint NOT NULL, + role_id bigint NOT NULL, + skill_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, cat integer @@ -647,7 +647,7 @@ ALTER SEQUENCE role_skills_id_seq OWNED BY role_skills.id; -- CREATE TABLE roles ( - id integer NOT NULL, + id bigint NOT NULL, name character varying(255) NOT NULL, ability character varying(255) NOT NULL, kind character varying(255) NOT NULL, @@ -690,7 +690,7 @@ CREATE TABLE schema_migrations ( -- CREATE TABLE skills ( - id integer NOT NULL, + id bigint NOT NULL, title character varying(255) NOT NULL, description text, original_row integer, @@ -723,10 +723,10 @@ ALTER SEQUENCE skills_id_seq OWNED BY skills.id; -- CREATE TABLE slugged_routes ( - id integer NOT NULL, + id bigint NOT NULL, slug character varying(255) NOT NULL, - organization_id integer, - user_id integer, + organization_id bigint, + user_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -756,7 +756,7 @@ ALTER SEQUENCE slugged_routes_id_seq OWNED BY slugged_routes.id; -- CREATE TABLE stripe_connect_accounts ( - id integer NOT NULL, + id bigint NOT NULL, business_name character varying(255), business_url character varying(255), charges_enabled boolean, @@ -771,7 +771,7 @@ CREATE TABLE stripe_connect_accounts ( support_phone character varying(255), support_url character varying(255), transfers_enabled boolean, - organization_id integer NOT NULL, + organization_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, verification_disabled_reason character varying(255), @@ -838,10 +838,10 @@ ALTER SEQUENCE stripe_connect_accounts_id_seq OWNED BY stripe_connect_accounts.i -- CREATE TABLE stripe_connect_cards ( - id integer NOT NULL, + id bigint NOT NULL, id_from_stripe character varying(255) NOT NULL, - stripe_connect_account_id integer NOT NULL, - stripe_platform_card_id integer NOT NULL, + stripe_connect_account_id bigint NOT NULL, + stripe_platform_card_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -871,7 +871,7 @@ ALTER SEQUENCE stripe_connect_cards_id_seq OWNED BY stripe_connect_cards.id; -- CREATE TABLE stripe_connect_charges ( - id integer NOT NULL, + id bigint NOT NULL, amount integer, amount_refunded integer, application_id_from_stripe character varying(255), @@ -892,9 +892,9 @@ CREATE TABLE stripe_connect_charges ( source_transfer_id_from_stripe character varying(255), statement_descriptor character varying(255), status character varying(255), - stripe_connect_account_id integer, - stripe_connect_customer_id integer NOT NULL, - user_id integer NOT NULL, + stripe_connect_account_id bigint, + stripe_connect_customer_id bigint NOT NULL, + user_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -924,13 +924,13 @@ ALTER SEQUENCE stripe_connect_charges_id_seq OWNED BY stripe_connect_charges.id; -- CREATE TABLE stripe_connect_customers ( - id integer NOT NULL, + id bigint NOT NULL, id_from_stripe character varying(255) NOT NULL, - stripe_connect_account_id integer NOT NULL, - stripe_platform_customer_id integer NOT NULL, + stripe_connect_account_id bigint NOT NULL, + stripe_platform_customer_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, - user_id integer NOT NULL + user_id bigint NOT NULL ); @@ -958,11 +958,11 @@ ALTER SEQUENCE stripe_connect_customers_id_seq OWNED BY stripe_connect_customers -- CREATE TABLE stripe_connect_plans ( - id integer NOT NULL, + id bigint NOT NULL, amount integer, id_from_stripe character varying(255) NOT NULL, name character varying(255), - project_id integer NOT NULL, + project_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, created integer @@ -993,15 +993,15 @@ ALTER SEQUENCE stripe_connect_plans_id_seq OWNED BY stripe_connect_plans.id; -- CREATE TABLE stripe_connect_subscriptions ( - id integer NOT NULL, + id bigint NOT NULL, application_fee_percent numeric, customer_id_from_stripe character varying(255), id_from_stripe character varying(255) NOT NULL, plan_id_from_stripe character varying(255) NOT NULL, quantity integer, status character varying(255), - stripe_connect_plan_id integer NOT NULL, - user_id integer, + stripe_connect_plan_id bigint NOT NULL, + user_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, cancelled_at integer, @@ -1037,7 +1037,7 @@ ALTER SEQUENCE stripe_connect_subscriptions_id_seq OWNED BY stripe_connect_subsc -- CREATE TABLE stripe_events ( - id integer NOT NULL, + id bigint NOT NULL, id_from_stripe character varying(255) NOT NULL, status character varying(255) DEFAULT 'unprocessed'::character varying, type character varying(255) NOT NULL, @@ -1075,7 +1075,7 @@ ALTER SEQUENCE stripe_events_id_seq OWNED BY stripe_events.id; -- CREATE TABLE stripe_external_accounts ( - id integer NOT NULL, + id bigint NOT NULL, id_from_stripe character varying(255) NOT NULL, account_id_from_stripe character varying(255) NOT NULL, account_holder_name character varying(255), @@ -1090,7 +1090,7 @@ CREATE TABLE stripe_external_accounts ( inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, default_for_currency boolean, - stripe_connect_account_id integer + stripe_connect_account_id bigint ); @@ -1118,13 +1118,13 @@ ALTER SEQUENCE stripe_external_accounts_id_seq OWNED BY stripe_external_accounts -- CREATE TABLE stripe_file_upload ( - id integer NOT NULL, + id bigint NOT NULL, id_from_stripe character varying(255) NOT NULL, purpose character varying(255), size integer, type character varying(255), url character varying(255), - stripe_connect_account_id integer, + stripe_connect_account_id bigint, created integer ); @@ -1153,7 +1153,7 @@ ALTER SEQUENCE stripe_file_upload_id_seq OWNED BY stripe_file_upload.id; -- CREATE TABLE stripe_invoices ( - id integer NOT NULL, + id bigint NOT NULL, amount_due integer, application_fee integer, attempt_count integer, @@ -1181,8 +1181,8 @@ CREATE TABLE stripe_invoices ( tax_percent double precision, total integer, webhooks_delievered_at integer, - stripe_connect_subscription_id integer NOT NULL, - user_id integer NOT NULL, + stripe_connect_subscription_id bigint NOT NULL, + user_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -1212,7 +1212,7 @@ ALTER SEQUENCE stripe_invoices_id_seq OWNED BY stripe_invoices.id; -- CREATE TABLE stripe_platform_cards ( - id integer NOT NULL, + id bigint NOT NULL, brand character varying(255), customer_id_from_stripe character varying(255), cvc_check character varying(255), @@ -1221,7 +1221,7 @@ CREATE TABLE stripe_platform_cards ( id_from_stripe character varying(255) NOT NULL, last4 character varying(255), name character varying(255), - user_id integer NOT NULL, + user_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -1251,12 +1251,12 @@ ALTER SEQUENCE stripe_platform_cards_id_seq OWNED BY stripe_platform_cards.id; -- CREATE TABLE stripe_platform_customers ( - id integer NOT NULL, + id bigint NOT NULL, currency character varying(255), delinquent boolean, email character varying(255), id_from_stripe character varying(255) NOT NULL, - user_id integer NOT NULL, + user_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, created integer @@ -1287,10 +1287,10 @@ ALTER SEQUENCE stripe_platform_customers_id_seq OWNED BY stripe_platform_custome -- CREATE TABLE task_lists ( - id integer NOT NULL, + id bigint NOT NULL, name character varying(255), "order" integer, - project_id integer, + project_id bigint, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, inbox boolean DEFAULT false @@ -1321,9 +1321,9 @@ ALTER SEQUENCE task_lists_id_seq OWNED BY task_lists.id; -- CREATE TABLE task_skills ( - id integer NOT NULL, - skill_id integer NOT NULL, - task_id integer NOT NULL, + id bigint NOT NULL, + skill_id bigint NOT NULL, + task_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -1353,20 +1353,20 @@ ALTER SEQUENCE task_skills_id_seq OWNED BY task_skills.id; -- CREATE TABLE tasks ( - id integer NOT NULL, + id bigint NOT NULL, body text, markdown text, number integer NOT NULL, status character varying(255) DEFAULT 'open'::character varying NOT NULL, title text NOT NULL, - project_id integer NOT NULL, - user_id integer NOT NULL, + project_id bigint NOT NULL, + user_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, - task_list_id integer, + task_list_id bigint, "order" integer, github_issue_number integer, - github_repo_id integer + github_repo_id bigint ); @@ -1394,9 +1394,9 @@ ALTER SEQUENCE tasks_id_seq OWNED BY tasks.id; -- CREATE TABLE user_categories ( - id integer NOT NULL, - user_id integer NOT NULL, - category_id integer NOT NULL, + id bigint NOT NULL, + user_id bigint NOT NULL, + category_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -1426,9 +1426,9 @@ ALTER SEQUENCE user_categories_id_seq OWNED BY user_categories.id; -- CREATE TABLE user_roles ( - id integer NOT NULL, - user_id integer NOT NULL, - role_id integer NOT NULL, + id bigint NOT NULL, + user_id bigint NOT NULL, + role_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -1458,9 +1458,9 @@ ALTER SEQUENCE user_roles_id_seq OWNED BY user_roles.id; -- CREATE TABLE user_skills ( - id integer NOT NULL, - user_id integer NOT NULL, - skill_id integer NOT NULL, + id bigint NOT NULL, + user_id bigint NOT NULL, + skill_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -1490,9 +1490,9 @@ ALTER SEQUENCE user_skills_id_seq OWNED BY user_skills.id; -- CREATE TABLE user_tasks ( - id integer NOT NULL, - task_id integer NOT NULL, - user_id integer NOT NULL, + id bigint NOT NULL, + task_id bigint NOT NULL, + user_id bigint NOT NULL, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -1522,7 +1522,7 @@ ALTER SEQUENCE user_tasks_id_seq OWNED BY user_tasks.id; -- CREATE TABLE users ( - id integer NOT NULL, + id bigint NOT NULL, username character varying(255), email character varying(255), encrypted_password character varying(255), diff --git a/test/fixtures/github/events/issue_comment_created_by_bot.json b/test/fixtures/github/events/issue_comment_created_by_bot.json new file mode 100644 index 000000000..b149e4fa3 --- /dev/null +++ b/test/fixtures/github/events/issue_comment_created_by_bot.json @@ -0,0 +1,182 @@ +{ + "action": "created", + "issue": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2/labels{/name}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2/comments", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2/events", + "html_url": "https://github.com/baxterthehacker/public-repo/issues/2", + "id": 73464126, + "number": 2, + "title": "Spelling error in the README file", + "user": { + "login": "baxterthehacker", + "id": 6752318, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/labels/bug", + "name": "bug", + "color": "fc2929" + } + ], + "state": "open", + "locked": false, + "assignee": null, + "milestone": null, + "comments": 1, + "created_at": "2015-05-05T23:40:28Z", + "updated_at": "2015-05-05T23:40:28Z", + "closed_at": null, + "body": "It looks like you accidently spelled 'commit' with two 't's." + }, + "comment": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments/99262140", + "html_url": "https://github.com/baxterthehacker/public-repo/issues/2#issuecomment-99262140", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2", + "id": 99262140, + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "Bot", + "site_admin": false + }, + "created_at": "2015-05-05T23:40:28Z", + "updated_at": "2015-05-05T23:40:28Z", + "body": "You are totally right! I'll get this fixed right away." + }, + "repository": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2015-05-05T23:40:12Z", + "updated_at": "2015-05-05T23:40:12Z", + "pushed_at": "2015-05-05T23:40:27Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 2, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + } +} diff --git a/test/fixtures/github/events/issues_opened_by_bot.json b/test/fixtures/github/events/issues_opened_by_bot.json new file mode 100644 index 000000000..6519a4a42 --- /dev/null +++ b/test/fixtures/github/events/issues_opened_by_bot.json @@ -0,0 +1,156 @@ +{ + "action": "opened", + "issue": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2/labels{/name}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2/comments", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/2/events", + "html_url": "https://github.com/baxterthehacker/public-repo/issues/2", + "id": 73464126, + "number": 2, + "title": "Spelling error in the README file", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "Bot", + "site_admin": false + }, + "labels": [ + { + "id": 208045946, + "url": "https://api.github.com/repos/baxterthehacker/public-repo/labels/bug", + "name": "bug", + "color": "fc2929", + "default": true + } + ], + "state": "open", + "locked": false, + "assignee": null, + "milestone": null, + "comments": 0, + "created_at": "2015-05-05T23:40:28Z", + "updated_at": "2015-05-05T23:40:28Z", + "closed_at": null, + "body": "It looks like you accidently spelled 'commit' with two 't's." + }, + "repository": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2015-05-05T23:40:12Z", + "updated_at": "2015-05-05T23:40:12Z", + "pushed_at": "2015-05-05T23:40:27Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 2, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + } +} diff --git a/test/lib/code_corps/accounts/accounts_test.exs b/test/lib/code_corps/accounts/accounts_test.exs index 1085c208e..7d76b81a2 100644 --- a/test/lib/code_corps/accounts/accounts_test.exs +++ b/test/lib/code_corps/accounts/accounts_test.exs @@ -15,6 +15,7 @@ defmodule CodeCorps.AccountsTest do assert user.id assert user.context == "github" + assert user.type == "user" end test "returns changeset if there was a validation error" do @@ -27,4 +28,12 @@ defmodule CodeCorps.AccountsTest do assert changeset.errors[:email] == {"has already been taken", []} end end + + describe "create_from_github_changeset/1" do + test "validates inclusion of type" do + params = %{"email" => "test@email.com", "type" => "Organization"} + changeset = Accounts.create_from_github_changeset(%User{}, params) + assert changeset.errors[:type] == {"is invalid", [validation: :inclusion]} + end + end end diff --git a/test/lib/code_corps/github/adapters/user_test.exs b/test/lib/code_corps/github/adapters/user_test.exs index f36843944..a89e6e5a5 100644 --- a/test/lib/code_corps/github/adapters/user_test.exs +++ b/test/lib/code_corps/github/adapters/user_test.exs @@ -8,14 +8,27 @@ defmodule CodeCorps.GitHub.Adapters.UserTest do alias CodeCorps.GitHub.Adapters.User describe "from_github_user/1" do - test "maps api payload correctly" do + test "maps API payload" do %{"issue" => %{"user" => payload}} = load_event_fixture("issues_opened") assert User.from_github_user(payload) == %{ github_id: payload["id"], github_username: payload["login"], - github_avatar_url: payload["avatar_url"] + github_avatar_url: payload["avatar_url"], + type: "user" # type gets transformed } end + + test "maps Bot type" do + assert User.from_github_user(%{"type" => "Bot"}) == %{type: "bot"} + end + + test "maps User type" do + assert User.from_github_user(%{"type" => "User"}) == %{type: "user"} + end + + test "does not map Organization type" do + assert User.from_github_user(%{"type" => "Organization"}) == %{type: "Organization"} + end end end diff --git a/test/lib/code_corps/github/event/issue_comment/user_linker_test.exs b/test/lib/code_corps/github/event/issue_comment/user_linker_test.exs index d20ab5651..ecf3a36bf 100644 --- a/test/lib/code_corps/github/event/issue_comment/user_linker_test.exs +++ b/test/lib/code_corps/github/event/issue_comment/user_linker_test.exs @@ -14,10 +14,44 @@ defmodule CodeCorps.GitHub.Event.IssueComment.UserLinkerTest do alias CodeCorps.GitHub.Adapters.User, as: UserAdapter @payload load_event_fixture("issue_comment_created") + @bot_payload load_event_fixture("issue_comment_created_by_bot") @user_payload @payload["comment"]["user"] + @bot_user_payload @bot_payload["comment"]["user"] describe "find_or_create_user/1" do - test "creates user if none is found" do + test "finds user by comment association" do + %{"comment" => %{"id" => github_id}} = @payload + user = insert(:user) + # multiple comments, but with same user is ok + insert_pair(:comment, github_id: github_id, user: user) + + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@payload) + + assert user.id == returned_user.id + end + + test "returns error if multiple users by comment association found" do + %{"comment" => %{"id" => github_id}} = @payload + + # multiple matched comments each with different user is not ok + insert_pair(:comment, github_id: github_id) + + assert {:error, :multiple_users} == + UserLinker.find_or_create_user(@payload) + end + + test "finds user by github id if none is found by comment association" do + attributes = UserAdapter.from_github_user(@user_payload) + preinserted_user = insert(:user, attributes) + + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@payload) + + assert preinserted_user.id == returned_user.id + + assert Repo.one(User) + end + + test "creates user if none is by comment or id association" do {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@payload) assert Repo.one(User) @@ -27,15 +61,35 @@ defmodule CodeCorps.GitHub.Event.IssueComment.UserLinkerTest do assert created_user.id == returned_user.id end - test "returns user if one is found" do - attributes = UserAdapter.from_github_user(@user_payload) - preinserted_user = insert(:user, attributes) + test "if comment created by bot, finds user by comment association" do + %{"comment" => %{ + "id" => github_id, + "user" => %{"id" => bot_user_github_id}}} = @bot_payload + %{user: preinserted_user} = insert(:comment, github_id: github_id) - {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@payload) + {:ok, %User{} = returned_user} = + UserLinker.find_or_create_user(@bot_payload) assert preinserted_user.id == returned_user.id + refute Repo.get_by(User, github_id: bot_user_github_id) + end + + test "if issue opened by bot, and no user by comment association, creates a bot user" do + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@bot_payload) + assert Repo.one(User) + + created_attributes = UserAdapter.from_github_user(@bot_user_payload) + created_user = Repo.get_by(User, created_attributes) + assert created_user.id == returned_user.id + end + + test "returns changeset if payload is somehow not as expected" do + bad_payload = @payload |> put_in(["comment", "user", "type"], "Organization") + + {:error, changeset} = UserLinker.find_or_create_user(bad_payload) + refute changeset.valid? end end end diff --git a/test/lib/code_corps/github/event/issue_comment_test.exs b/test/lib/code_corps/github/event/issue_comment_test.exs index 81fc0e316..c42bac300 100644 --- a/test/lib/code_corps/github/event/issue_comment_test.exs +++ b/test/lib/code_corps/github/event/issue_comment_test.exs @@ -277,17 +277,16 @@ defmodule CodeCorps.GitHub.Event.IssueCommentTest do %{ "issue" => %{ "body" => issue_markdown, "title" => issue_title, "number" => issue_number, "state" => issue_state, - "user" => %{"id" => issue_user_github_id} + "user" => %{"id" => user_github_id} }, "comment" => %{ "body" => comment_markdown, "id" => comment_github_id, - "user" => %{"id" => comment_user_github_id} + "user" => _same_as_issue_user_payload }, "repository" => %{"id" => repo_github_id} } = @payload - comment_user = insert(:user, github_id: comment_user_github_id) - issue_user = insert(:user, github_id: issue_user_github_id) + user = insert(:user, github_id: user_github_id) github_repo = insert(:github_repo, github_id: repo_github_id) [%{project: project_1}, %{project: project_2}, %{project: _project_3}] = @@ -301,11 +300,11 @@ defmodule CodeCorps.GitHub.Event.IssueCommentTest do end) # there's a task and comment for project 1 - task_1 = insert(:task, project: project_1, user: issue_user, github_repo: github_repo, github_issue_number: issue_number) - comment_1 = insert(:comment, task: task_1, user: comment_user, github_id: comment_github_id) + task_1 = insert(:task, project: project_1, user: user, github_repo: github_repo, github_issue_number: issue_number) + comment_1 = insert(:comment, task: task_1, user: user, github_id: comment_github_id) # there is only a task for project 2 - task_2 = insert(:task, project: project_2, user: issue_user, github_repo: github_repo, github_issue_number: issue_number) + task_2 = insert(:task, project: project_2, user: user, github_repo: github_repo, github_issue_number: issue_number) {:ok, comments} = IssueComment.handle(@event, @payload) @@ -321,7 +320,7 @@ defmodule CodeCorps.GitHub.Event.IssueCommentTest do assert task.project_id in project_ids assert task.status == issue_state assert task.title == issue_title - assert task.user_id == issue_user.id + assert task.user_id == user.id assert task.github_repo_id == github_repo.id end) @@ -334,7 +333,7 @@ defmodule CodeCorps.GitHub.Event.IssueCommentTest do assert comment.body assert comment.github_id == comment_github_id assert comment.markdown == comment_markdown - assert comment.user_id == comment_user.id + assert comment.user_id == user.id end) comment_ids = comments |> Enum.map(&Map.get(&1, :id)) diff --git a/test/lib/code_corps/github/event/issues/user_linker_test.exs b/test/lib/code_corps/github/event/issues/user_linker_test.exs index b29b81624..f97437b11 100644 --- a/test/lib/code_corps/github/event/issues/user_linker_test.exs +++ b/test/lib/code_corps/github/event/issues/user_linker_test.exs @@ -14,10 +14,54 @@ defmodule CodeCorps.GitHub.Event.Issues.UserLinkerTest do alias CodeCorps.GitHub.Adapters.User, as: UserAdapter @payload load_event_fixture("issues_opened") + @bot_payload load_event_fixture("issues_opened_by_bot") @user_payload @payload["issue"]["user"] + @bot_user_payload @bot_payload["issue"]["user"] describe "find_or_create_user/1" do - test "creates user if none is found" do + test "finds user by task association" do + %{ + "issue" => %{"number" => issue_number}, + "repository" => %{"id" => github_repo_id} + } = @payload + + user = insert(:user) + repo = insert(:github_repo, github_id: github_repo_id) + # multiple tasks, all with same user is ok + insert_pair( + :task, user: user, github_repo: repo, github_issue_number: issue_number) + + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@payload) + + assert user.id == returned_user.id + end + + test "returns error if multiple users by task association found" do + %{ + "issue" => %{"number" => issue_number}, + "repository" => %{"id" => github_repo_id} + } = @payload + + repo = insert(:github_repo, github_id: github_repo_id) + # multiple tasks, each with different user is not ok + insert_pair(:task, github_repo: repo, github_issue_number: issue_number) + + assert {:error, :multiple_users} == + UserLinker.find_or_create_user(@payload) + end + + test "returns user by github id if no user by task association found" do + attributes = UserAdapter.from_github_user(@user_payload) + preinserted_user = insert(:user, attributes) + + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@payload) + + assert preinserted_user.id == returned_user.id + + assert Repo.one(User) + end + + test "creates user if none is found by any other method" do {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@payload) assert Repo.one(User) @@ -27,15 +71,43 @@ defmodule CodeCorps.GitHub.Event.Issues.UserLinkerTest do assert created_user.id == returned_user.id end - test "returns user if one is found" do - attributes = UserAdapter.from_github_user(@user_payload) - preinserted_user = insert(:user, attributes) + test "if issue opened by bot, finds user by task association" do + %{ + "issue" => %{ + "number" => issue_number, "user" => %{"id" => bot_user_github_id}}, + "repository" => %{"id" => github_repo_id} + } = @bot_payload - {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@payload) + preinserted_user = insert(:user) + repo = insert(:github_repo, github_id: github_repo_id) + insert( + :task, + user: preinserted_user, github_repo: repo, + github_issue_number: issue_number) + + {:ok, %User{} = returned_user} = + UserLinker.find_or_create_user(@bot_payload) assert preinserted_user.id == returned_user.id + refute Repo.get_by(User, github_id: bot_user_github_id) + end + + test "if issue opened by bot, and no user by task association, creates a bot user" do + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(@bot_payload) + assert Repo.one(User) + + created_attributes = UserAdapter.from_github_user(@bot_user_payload) + created_user = Repo.get_by(User, created_attributes) + assert created_user.id == returned_user.id + end + + test "returns changeset if payload is somehow not as expected" do + bad_payload = @payload |> put_in(["issue", "user", "type"], "Organization") + + {:error, changeset} = UserLinker.find_or_create_user(bad_payload) + refute changeset.valid? end end end diff --git a/test/lib/code_corps/policy/task_skill_test.exs b/test/lib/code_corps/policy/task_skill_test.exs index f6a9e754a..cde474485 100644 --- a/test/lib/code_corps/policy/task_skill_test.exs +++ b/test/lib/code_corps/policy/task_skill_test.exs @@ -5,8 +5,6 @@ defmodule CodeCorps.Policy.TaskSkillTest do import CodeCorps.Policy.TaskSkill, only: [create?: 2, delete?: 2] - alias CodeCorps.TaskSkill - describe "create?" do test "returns false when user is not member of project" do user = insert(:user)