From 320e496263d99ca58a063214f08d2feb21b5c95e Mon Sep 17 00:00:00 2001 From: Josh Smith Date: Mon, 16 Oct 2017 15:58:27 -0700 Subject: [PATCH 1/2] Add GithubPullRequest syncing --- ...pp_installation.ex => app_installation.ex} | 2 +- .../github/adapters/pull_request.ex | 39 ++ .../adapters/{github_repo.ex => repo.ex} | 2 +- .../event/installation/changeset_builder.ex | 4 +- .../github/event/installation/repos.ex | 4 +- lib/code_corps/github/event/pull_request.ex | 96 ++++ .../event/pull_request/changeset_builder.ex | 83 +++ .../event/pull_request/pull_request_linker.ex | 55 ++ .../github/event/pull_request/task_syncer.ex | 65 +++ .../github/event/pull_request/user_linker.ex | 69 +++ .../github/event/pull_request/validator.ex | 27 + .../github/webhook/event_support.ex | 4 +- lib/code_corps/github/webhook/handler.ex | 2 + .../model/github_app_installation.ex | 12 +- lib/code_corps/model/github_pull_request.ex | 73 +++ lib/code_corps/model/task.ex | 1 + .../github_pull_request_controller.ex | 23 + lib/code_corps_web/router.ex | 1 + .../views/github_pull_request_view.ex | 9 + lib/code_corps_web/views/task_view.ex | 3 +- ...1016223356_create_github_pull_requests.exs | 43 ++ ...71016235656_add_pull_requests_to_tasks.exs | 15 + priv/repo/structure.sql | 103 +++- .../github/events/pull_request_closed.json | 415 ++++++++++++++ .../github/events/pull_request_edited.json | 415 ++++++++++++++ .../github/events/pull_request_opened.json | 415 ++++++++++++++ .../events/pull_request_opened_by_bot.json | 415 ++++++++++++++ .../github/events/pull_request_reopened.json | 415 ++++++++++++++ ...ion_test.exs => app_installation_test.exs} | 6 +- .../github/adapters/pull_request_test.exs | 47 ++ .../{github_repo_test.exs => repo_test.exs} | 6 +- .../github/event/installation/repos_test.exs | 6 +- .../pull_request/changeset_builder_test.exs | 82 +++ .../pull_request/pull_request_linker_test.exs | 58 ++ .../event/pull_request/task_syncer_test.exs | 69 +++ .../event/pull_request/user_linker_test.exs | 119 ++++ .../event/pull_request/validator_test.exs | 24 + .../github/event/pull_request_test.exs | 513 ++++++++++++++++++ .../model/github_pull_request_test.exs | 31 ++ .../github_pull_request_controller_test.exs | 39 ++ .../views/github_pull_request_view_test.exs | 35 ++ .../code_corps_web/views/task_view_test.exs | 9 +- test/support/factories.ex | 15 + 43 files changed, 3841 insertions(+), 28 deletions(-) rename lib/code_corps/github/adapters/{github_app_installation.ex => app_installation.ex} (93%) create mode 100644 lib/code_corps/github/adapters/pull_request.ex rename lib/code_corps/github/adapters/{github_repo.ex => repo.ex} (89%) create mode 100644 lib/code_corps/github/event/pull_request.ex create mode 100644 lib/code_corps/github/event/pull_request/changeset_builder.ex create mode 100644 lib/code_corps/github/event/pull_request/pull_request_linker.ex create mode 100644 lib/code_corps/github/event/pull_request/task_syncer.ex create mode 100644 lib/code_corps/github/event/pull_request/user_linker.ex create mode 100644 lib/code_corps/github/event/pull_request/validator.ex create mode 100644 lib/code_corps/model/github_pull_request.ex create mode 100644 lib/code_corps_web/controllers/github_pull_request_controller.ex create mode 100644 lib/code_corps_web/views/github_pull_request_view.ex create mode 100644 priv/repo/migrations/20171016223356_create_github_pull_requests.exs create mode 100644 priv/repo/migrations/20171016235656_add_pull_requests_to_tasks.exs create mode 100644 test/fixtures/github/events/pull_request_closed.json create mode 100644 test/fixtures/github/events/pull_request_edited.json create mode 100644 test/fixtures/github/events/pull_request_opened.json create mode 100644 test/fixtures/github/events/pull_request_opened_by_bot.json create mode 100644 test/fixtures/github/events/pull_request_reopened.json rename test/lib/code_corps/github/adapters/{github_app_installation_test.exs => app_installation_test.exs} (77%) create mode 100644 test/lib/code_corps/github/adapters/pull_request_test.exs rename test/lib/code_corps/github/adapters/{github_repo_test.exs => repo_test.exs} (80%) create mode 100644 test/lib/code_corps/github/event/pull_request/changeset_builder_test.exs create mode 100644 test/lib/code_corps/github/event/pull_request/pull_request_linker_test.exs create mode 100644 test/lib/code_corps/github/event/pull_request/task_syncer_test.exs create mode 100644 test/lib/code_corps/github/event/pull_request/user_linker_test.exs create mode 100644 test/lib/code_corps/github/event/pull_request/validator_test.exs create mode 100644 test/lib/code_corps/github/event/pull_request_test.exs create mode 100644 test/lib/code_corps/model/github_pull_request_test.exs create mode 100644 test/lib/code_corps_web/controllers/github_pull_request_controller_test.exs create mode 100644 test/lib/code_corps_web/views/github_pull_request_view_test.exs diff --git a/lib/code_corps/github/adapters/github_app_installation.ex b/lib/code_corps/github/adapters/app_installation.ex similarity index 93% rename from lib/code_corps/github/adapters/github_app_installation.ex rename to lib/code_corps/github/adapters/app_installation.ex index e0b5550bb..571055f93 100644 --- a/lib/code_corps/github/adapters/github_app_installation.ex +++ b/lib/code_corps/github/adapters/app_installation.ex @@ -1,4 +1,4 @@ -defmodule CodeCorps.GitHub.Adapters.GithubAppInstallation do +defmodule CodeCorps.GitHub.Adapters.AppInstallation do @moduledoc """ Module used to convert GitHub payloads into attributes for a `GithubAppInstallation`. diff --git a/lib/code_corps/github/adapters/pull_request.ex b/lib/code_corps/github/adapters/pull_request.ex new file mode 100644 index 000000000..993c85e52 --- /dev/null +++ b/lib/code_corps/github/adapters/pull_request.ex @@ -0,0 +1,39 @@ +defmodule CodeCorps.GitHub.Adapters.PullRequest do + + @mapping [ + {:additions, ["additions"]}, + {:body, ["body"]}, + {:changed_files, ["changed_files"]}, + {:closed_at, ["closed_at"]}, + {:comments, ["comments"]}, + {:comments_url, ["comments_url"]}, + {:commits, ["commits"]}, + {:commits_url, ["commits_url"]}, + {:deletions, ["deletions"]}, + {:diff_url, ["diff_url"]}, + {:github_created_at, ["created_at"]}, + {:github_id, ["id"]}, + {:github_updated_at, ["updated_at"]}, + {:html_url, ["html_url"]}, + {:issue_url, ["issue_url"]}, + {:locked, ["locked"]}, + {:merge_commit_sha, ["merge_commit_sha"]}, + {:mergeable_state, ["mergeable_state"]}, + {:merged, ["merged"]}, + {:merged_at, ["merged_at"]}, + {:number, ["number"]}, + {:patch_url, ["patch_url"]}, + {:review_comment_url, ["review_comment_url"]}, + {:review_comments, ["review_comments"]}, + {:review_comments_url, ["review_comments_url"]}, + {:state, ["state"]}, + {:statuses_url, ["statuses_url"]}, + {:title, ["title"]}, + {:url, ["url"]} + ] + + @spec from_api(map) :: map + def from_api(%{} = payload) do + payload |> CodeCorps.Adapter.MapTransformer.transform(@mapping) + end +end diff --git a/lib/code_corps/github/adapters/github_repo.ex b/lib/code_corps/github/adapters/repo.ex similarity index 89% rename from lib/code_corps/github/adapters/github_repo.ex rename to lib/code_corps/github/adapters/repo.ex index b383bf35a..b22de18fe 100644 --- a/lib/code_corps/github/adapters/github_repo.ex +++ b/lib/code_corps/github/adapters/repo.ex @@ -1,4 +1,4 @@ -defmodule CodeCorps.GitHub.Adapters.GithubRepo do +defmodule CodeCorps.GitHub.Adapters.Repo do @mapping [ {:github_account_avatar_url, ["owner", "avatar_url"]}, diff --git a/lib/code_corps/github/event/installation/changeset_builder.ex b/lib/code_corps/github/event/installation/changeset_builder.ex index c2574ac12..2f1b4a4be 100644 --- a/lib/code_corps/github/event/installation/changeset_builder.ex +++ b/lib/code_corps/github/event/installation/changeset_builder.ex @@ -8,7 +8,7 @@ defmodule CodeCorps.GitHub.Event.Installation.ChangesetBuilder do GithubAppInstallation, User } - alias CodeCorps.GitHub.Adapters.GithubAppInstallation, as: GithubAppInstallationAdapter + alias CodeCorps.GitHub.Adapters.AppInstallation, as: AppInstallationAdapter alias Ecto.Changeset @doc """ @@ -20,7 +20,7 @@ defmodule CodeCorps.GitHub.Event.Installation.ChangesetBuilder do %GithubAppInstallation{} = github_app_installation, %{} = payload) do - attrs = GithubAppInstallationAdapter.from_installation_event(payload) + attrs = AppInstallationAdapter.from_installation_event(payload) github_app_installation |> Changeset.change(attrs) diff --git a/lib/code_corps/github/event/installation/repos.ex b/lib/code_corps/github/event/installation/repos.ex index 6608ac173..1028eecaa 100644 --- a/lib/code_corps/github/event/installation/repos.ex +++ b/lib/code_corps/github/event/installation/repos.ex @@ -12,7 +12,7 @@ defmodule CodeCorps.GitHub.Event.Installation.Repos do Repo } - alias CodeCorps.GitHub.Adapters.GithubRepo, as: GithubRepoAdapter + alias CodeCorps.GitHub.Adapters.Repo, as: RepoAdapter alias Ecto.{Changeset, Multi} @@ -51,7 +51,7 @@ defmodule CodeCorps.GitHub.Event.Installation.Repos do # transaction step 2 @spec adapt_api_repo_list(map) :: {:ok, list(map)} defp adapt_api_repo_list(%{api_response: repositories}) do - adapter_results = repositories |> Enum.map(&GithubRepoAdapter.from_api/1) + adapter_results = repositories |> Enum.map(&RepoAdapter.from_api/1) {:ok, adapter_results} end diff --git a/lib/code_corps/github/event/pull_request.ex b/lib/code_corps/github/event/pull_request.ex new file mode 100644 index 000000000..9772e3833 --- /dev/null +++ b/lib/code_corps/github/event/pull_request.ex @@ -0,0 +1,96 @@ +defmodule CodeCorps.GitHub.Event.PullRequest do + @moduledoc ~S""" + In charge of handling a GitHub Webhook payload for the PullRequest event type + + [https://developer.github.com/v3/activity/events/types/#pullrequestevent](https://developer.github.com/v3/activity/events/types/#pullrequestevent) + """ + + @behaviour CodeCorps.GitHub.Event.Handler + + alias CodeCorps.{ + GitHub.Event.Common.RepoFinder, + GitHub.Event.PullRequest.PullRequestLinker, + GitHub.Event.PullRequest.TaskSyncer, + GitHub.Event.PullRequest.UserLinker, + GitHub.Event.PullRequest.Validator, + Repo, + Task + } + alias Ecto.Multi + + @type outcome :: {:ok, list(Task.t)} | + {:error, :not_fully_implemented} | + {:error, :unexpected_action} | + {:error, :unexpected_payload} | + {:error, :repository_not_found} | + {:error, :validation_error_on_inserting_user} | + {:error, :multiple_github_users_matched_same_cc_user} | + {:error, :validation_error_on_syncing_tasks} | + {:error, :unexpected_transaction_outcome} + + @doc ~S""" + Handles the "PullRequest" GitHub webhook + + The process is as follows + - validate the payload is structured as expected + - validate the action is properly supported + - match payload with affected `CodeCorps.GithubRepo` record using + `CodeCorps.GitHub.Event.Common.RepoFinder` + - match with a `CodeCorps.User` using + `CodeCorps.GitHub.Event.PullRequest.UserLinker` + - for each `CodeCorps.ProjectGithubRepo` belonging to matched repo + - match and update, or create a `CodeCorps.Task` on the associated + `CodeCorps.Project` + + If the process runs all the way through, the function will return an `:ok` + tuple with a list of affected (created or updated) tasks. + + If it fails, it will instead return an `:error` tuple, where the second + element is the atom indicating a reason. + """ + @spec handle(map) :: outcome + def handle(payload) do + Multi.new + |> Multi.run(:payload, fn _ -> payload |> validate_payload() end) + |> Multi.run(:action, fn _ -> payload |> validate_action() end) + |> Multi.run(:repo, fn _ -> RepoFinder.find_repo(payload) end) + |> Multi.run(:pull_request, fn %{repo: github_repo} -> link_pull_request(github_repo, payload) end) + |> Multi.run(:user, fn %{pull_request: github_pull_request} -> UserLinker.find_or_create_user(github_pull_request, payload) end) + |> Multi.run(:tasks, fn %{pull_request: github_pull_request, user: user} -> github_pull_request |> TaskSyncer.sync_all(user, payload) end) + |> Repo.transaction + |> marshall_result() + end + + @spec link_pull_request(GithubRepo.t, map) :: {:ok, GithubIssue.t} | {:error, Ecto.Changeset.t} + defp link_pull_request(github_repo, %{"pull_request" => attrs}) do + PullRequestLinker.create_or_update_pull_request(github_repo, attrs) + end + + @spec marshall_result(tuple) :: tuple + defp marshall_result({:ok, %{tasks: tasks}}), do: {:ok, tasks} + defp marshall_result({:error, :payload, :invalid, _steps}), do: {:error, :unexpected_payload} + defp marshall_result({:error, :action, :not_fully_implemented, _steps}), do: {:error, :not_fully_implemented} + defp marshall_result({:error, :action, :unexpected_action, _steps}), do: {:error, :unexpected_action} + defp marshall_result({:error, :repo, :unmatched_project, _steps}), do: {:ok, []} + defp marshall_result({:error, :repo, :unmatched_repository, _steps}), do: {:error, :repository_not_found} + defp marshall_result({:error, :user, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_user} + defp marshall_result({:error, :user, :multiple_users, _steps}), do: {:error, :multiple_github_users_matched_same_cc_user} + defp marshall_result({:error, :tasks, {_tasks, _errors}, _steps}), do: {:error, :validation_error_on_syncing_tasks} + defp marshall_result({:error, _errored_step, _error_response, _steps}), do: {:error, :unexpected_transaction_outcome} + + @implemented_actions ~w(opened closed edited reopened) + @unimplemented_actions ~w(assigned unassigned review_requested review_request_removed labeled unlabeled) + + @spec validate_action(map) :: {:ok, :implemented} | {:error, :not_fully_implemented | :unexpected_action} + defp validate_action(%{"action" => action}) when action in @implemented_actions, do: {:ok, :implemented} + defp validate_action(%{"action" => action}) when action in @unimplemented_actions, do: {:error, :not_fully_implemented} + defp validate_action(_payload), do: {:error, :unexpected_action} + + @spec validate_payload(map) :: {:ok, :valid} | {:error, :invalid} + defp validate_payload(%{} = payload) do + case payload |> Validator.valid? do + true -> {:ok, :valid} + false -> {:error, :invalid} + end + end +end diff --git a/lib/code_corps/github/event/pull_request/changeset_builder.ex b/lib/code_corps/github/event/pull_request/changeset_builder.ex new file mode 100644 index 000000000..4aad1c0d6 --- /dev/null +++ b/lib/code_corps/github/event/pull_request/changeset_builder.ex @@ -0,0 +1,83 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.ChangesetBuilder do + @moduledoc ~S""" + In charge of building a `Changeset` to update a `Task` with, when handling an + PullRequest webhook. + """ + + alias CodeCorps.{ + GithubPullRequest, + ProjectGithubRepo, + Repo, + Services.MarkdownRendererService, + Task, + TaskList, + User, + Validators.TimeValidator + } + alias CodeCorps.GitHub.Adapters.Task, as: TaskAdapter + alias Ecto.Changeset + + @doc ~S""" + Constructs a changeset for syncing a `Task` when processing a PullRequest + webhook. + + The changeset can be used to create or update a `Task` + """ + @spec build_changeset(Task.t, map, GithubPullRequest.t, ProjectGithubRepo.t, User.t) :: Changeset.t + def build_changeset( + %Task{id: task_id} = task, + %{"pull_request" => pull_request_attrs}, + %GithubPullRequest{} = github_pull_request, + %ProjectGithubRepo{} = project_github_repo, + %User{} = user) do + + case is_nil(task_id) do + true -> create_changeset(task, pull_request_attrs, github_pull_request, project_github_repo, user) + false -> update_changeset(task, pull_request_attrs) + end + end + + @create_attrs ~w(created_at markdown modified_at status title)a + @spec create_changeset(Task.t, map, GithubPullRequest.t, ProjectGithubRepo.t, User.t) :: Changeset.t + defp create_changeset( + %Task{} = task, + %{} = pull_request_attrs, + %GithubPullRequest{id: github_pull_request_id}, + %ProjectGithubRepo{project_id: project_id, github_repo_id: github_repo_id}, + %User{id: user_id}) do + + %TaskList{id: task_list_id} = + TaskList |> Repo.get_by(project_id: project_id, inbox: true) + + task + |> Changeset.cast(TaskAdapter.from_api(pull_request_attrs), @create_attrs) + |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) + |> Changeset.put_change(:created_from, "github") + |> Changeset.put_change(:modified_from, "github") + |> Changeset.put_change(:github_pull_request_id, github_pull_request_id) + |> Changeset.put_change(:github_repo_id, github_repo_id) + |> Changeset.put_change(:project_id, project_id) + |> Changeset.put_change(:task_list_id, task_list_id) + |> Changeset.put_change(:user_id, user_id) + |> Changeset.validate_required([:project_id, :task_list_id, :title, :user_id]) + |> Changeset.assoc_constraint(:github_pull_request) + |> Changeset.assoc_constraint(:github_repo) + |> Changeset.assoc_constraint(:project) + |> Changeset.assoc_constraint(:task_list) + |> Changeset.assoc_constraint(:user) + end + + @update_attrs ~w(markdown modified_at status title)a + @spec update_changeset(Task.t, map) :: Changeset.t + defp update_changeset(%Task{} = task, %{} = pull_request_attrs) do + task + |> Changeset.cast(TaskAdapter.from_api(pull_request_attrs), @update_attrs) + |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) + |> Changeset.put_change(:modified_from, "github") + |> TimeValidator.validate_time_after(:modified_at) + |> Changeset.validate_required([:project_id, :title, :user_id]) + |> Changeset.assoc_constraint(:github_repo) + |> Changeset.assoc_constraint(:project) + |> Changeset.assoc_constraint(:user) + end +end diff --git a/lib/code_corps/github/event/pull_request/pull_request_linker.ex b/lib/code_corps/github/event/pull_request/pull_request_linker.ex new file mode 100644 index 000000000..3637084a9 --- /dev/null +++ b/lib/code_corps/github/event/pull_request/pull_request_linker.ex @@ -0,0 +1,55 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.PullRequestLinker do + @moduledoc ~S""" + In charge of finding a pull request to link with a `GithubPullRequest` record + when processing the PullRequest webhook. + + The only entry point is `create_or_update_pull_request/1`. + """ + + alias CodeCorps.{ + GithubPullRequest, + GithubRepo, + Repo + } + + alias CodeCorps.GitHub.Adapters.PullRequest, as: PullRequestAdapter + + @typep linking_result :: {:ok, GithubPullRequest.t} | + {:error, Ecto.Changeset.t} + + @doc ~S""" + Finds or creates a `GithubPullRequest` using the data in a GitHub PullRequest + payload. + + The process is as follows: + + - Search for the pull request in our database with the payload data. + - If we return a single `GithubPullRequest`, then the `GithubPullRequest` + should be updated. + - If there are no matching `GithubPullRequest` records, then a + `GithubPullRequest`should be created. + """ + @spec create_or_update_pull_request(GithubRepo.t, map) :: linking_result + def create_or_update_pull_request(%GithubRepo{} = github_repo, %{"id" => github_pull_request_id} = attrs) do + params = PullRequestAdapter.from_api(attrs) + + case Repo.get_by(GithubPullRequest, github_id: github_pull_request_id) do + nil -> create_pull_request(github_repo, params) + %GithubPullRequest{} = pull_request -> update_pull_request(pull_request, params) + end + end + + defp create_pull_request(%GithubRepo{id: github_repo_id}, params) do + params = Map.put(params, :github_repo_id, github_repo_id) + + %GithubPullRequest{} + |> GithubPullRequest.create_changeset(params) + |> Repo.insert + end + + defp update_pull_request(%GithubPullRequest{} = github_pull_request, params) do + github_pull_request + |> GithubPullRequest.update_changeset(params) + |> Repo.update + end +end diff --git a/lib/code_corps/github/event/pull_request/task_syncer.ex b/lib/code_corps/github/event/pull_request/task_syncer.ex new file mode 100644 index 000000000..66cf9573e --- /dev/null +++ b/lib/code_corps/github/event/pull_request/task_syncer.ex @@ -0,0 +1,65 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.TaskSyncer do + alias CodeCorps.{ + GithubPullRequest, + GithubRepo, + GitHub.Event.Common.ResultAggregator, + GitHub.Event.PullRequest.ChangesetBuilder, + ProjectGithubRepo, + Task, + User, + Repo + } + + alias Ecto.Changeset + + @type outcome :: {:ok, list(Task.t)} | + {:error, {list(Task.t), list(Changeset.t)}} + + @doc """ + When provided a `CodeCorps.GithubPullRequest`, a `CodeCorps.User` and a + GitHub API payload, for each `CodeCorps.Project` associated to that + `CodeCorps.GithubRepo` via a `CodeCorps.ProjectGithubRepo`, it + creates or updates a `CodeCorps.Task`. + """ + @spec sync_all(GithubPullRequest.t, User.t, map) :: {:ok, list(Task.t)} + def sync_all(%GithubPullRequest{} = github_pull_request, %User{} = user, %{} = payload) do + + %GithubPullRequest{ + github_repo: %GithubRepo{project_github_repos: project_github_repos} + } = github_pull_request |> Repo.preload(github_repo: :project_github_repos) + + project_github_repos + |> Enum.map(&sync(github_pull_request, &1, user, payload)) + |> ResultAggregator.aggregate + end + + @spec sync(GithubPullRequest.t, ProjectGithubRepo.t, User.t, map) :: {:ok, ProjectGithubRepo.t} | {:error, Changeset.t} + defp sync(%GithubPullRequest{} = github_pull_request, %ProjectGithubRepo{} = project_github_repo, %User{} = user, %{} = payload) do + project_github_repo + |> find_or_init_task(github_pull_request) + |> ChangesetBuilder.build_changeset(payload, github_pull_request, project_github_repo, user) + |> commit() + end + + @spec find_or_init_task(ProjectGithubRepo.t, GithubPullRequest.t) :: Task.t + defp find_or_init_task( + %ProjectGithubRepo{project_id: project_id, github_repo_id: github_repo_id}, + %GithubPullRequest{id: github_pull_request_id} + ) do + + query_params = [ + github_pull_request_id: github_pull_request_id, + github_repo_id: github_repo_id, + project_id: project_id + ] + + case Task |> Repo.get_by(query_params) do + nil -> %Task{} + %Task{} = task -> task + end + end + + @spec commit(Changeset.t) :: {:ok, Task.t} | {:error, Changeset.t} + defp commit(%Changeset{data: %Task{id: nil}} = changeset), do: changeset |> Repo.insert + defp commit(%Changeset{} = changeset), do: changeset |> Repo.update +end diff --git a/lib/code_corps/github/event/pull_request/user_linker.ex b/lib/code_corps/github/event/pull_request/user_linker.ex new file mode 100644 index 000000000..ee5b10278 --- /dev/null +++ b/lib/code_corps/github/event/pull_request/user_linker.ex @@ -0,0 +1,69 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.UserLinker do + @moduledoc ~S""" + In charge of finding a user to link with a `CodeCorps.Task` when processing + a PullRequest webhook. + + The only entry point is `find_or_create_user/1`. + """ + + import Ecto.Query + + alias CodeCorps.{ + Accounts, + GithubPullRequest, + 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 in a `GithubPullRequest` record and a + Github Issue 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 PR should be related to that user. + - If there are no matching users, then the PR 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. + - If there are multiple matching users, this is an unexpected scenario and + should error out. + """ + @spec find_or_create_user(GithubPullRequest.t, map) :: linking_result + def find_or_create_user(%GithubPullRequest{} = pull_request, %{"pull_request" => %{"user" => user_attrs}}) do + pull_request + |> match_users + |> marshall_response(user_attrs) + end + + @spec match_users(GithubPullRequest.t) :: list(User.t) + defp match_users(%GithubPullRequest{id: github_pull_request_id}) do + + query = from u in User, + distinct: u.id, + join: t in Task, on: u.id == t.user_id, where: t.github_pull_request_id == ^github_pull_request_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_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/pull_request/validator.ex b/lib/code_corps/github/event/pull_request/validator.ex new file mode 100644 index 000000000..b36565b4e --- /dev/null +++ b/lib/code_corps/github/event/pull_request/validator.ex @@ -0,0 +1,27 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.Validator do + @moduledoc ~S""" + In charge of validatng a GitHub PullRequest webhook payload. + + https://developer.github.com/v3/activity/events/types/#pullrequestevent + """ + + @doc ~S""" + Returns `true` if all keys required to properly handle an PullRequest webhook + are present in the provided payload. + """ + @spec valid?(map) :: boolean + def valid?(%{ + "action" => _, + "pull_request" => %{ + "id" => _, + "title" => _, + "user" => %{ + "id" => _ + } + }, + "repository" => %{ + "id" => _ + } + }), do: true + def valid?(_), do: false +end diff --git a/lib/code_corps/github/webhook/event_support.ex b/lib/code_corps/github/webhook/event_support.ex index 99b05495c..5f057ae5b 100644 --- a/lib/code_corps/github/webhook/event_support.ex +++ b/lib/code_corps/github/webhook/event_support.ex @@ -3,7 +3,9 @@ defmodule CodeCorps.GitHub.Webhook.EventSupport do Determines event support for a GitHub event type """ - @supported_events ~w(installation installation_repositories issue_comment issues) + @supported_events ~w( + installation installation_repositories issue_comment issues pull_request + ) @type support_status :: :supported | :unsupported diff --git a/lib/code_corps/github/webhook/handler.ex b/lib/code_corps/github/webhook/handler.ex index a6c4c2129..128a2a9f7 100644 --- a/lib/code_corps/github/webhook/handler.ex +++ b/lib/code_corps/github/webhook/handler.ex @@ -10,6 +10,7 @@ defmodule CodeCorps.GitHub.Webhook.Handler do GitHub.Event.InstallationRepositories, GitHub.Event.IssueComment, GitHub.Event.Issues, + GitHub.Event.PullRequest, GitHub.Webhook.EventSupport, Repo } @@ -51,4 +52,5 @@ defmodule CodeCorps.GitHub.Webhook.Handler do defp do_handle(payload, "installation_repositories"), do: InstallationRepositories.handle(payload) defp do_handle(payload, "issue_comment"), do: IssueComment.handle(payload) defp do_handle(payload, "issues"), do: Issues.handle(payload) + defp do_handle(payload, "pull_request"), do: PullRequest.handle(payload) end diff --git a/lib/code_corps/model/github_app_installation.ex b/lib/code_corps/model/github_app_installation.ex index 066fc1329..feaae9e91 100644 --- a/lib/code_corps/model/github_app_installation.ex +++ b/lib/code_corps/model/github_app_installation.ex @@ -9,22 +9,18 @@ defmodule CodeCorps.GithubAppInstallation do schema "github_app_installations" do field :access_token, :string field :access_token_expires_at, :utc_datetime - field :github_id, :integer - field :github_account_avatar_url, :string field :github_account_id, :integer field :github_account_login, :string field :github_account_type, :string - + field :github_id, :integer field :installed, :boolean + field :origin, :string, default: "codecorps" # "codecorps" or "github" field :sender_github_id, :integer # "unprocessed", "processing", "processed" or "errored" field :state, :string, default: "unprocessed" - # "codecorps" or "github" - field :origin, :string, default: "codecorps" - belongs_to :project, CodeCorps.Project # The originating project belongs_to :user, CodeCorps.User @@ -40,11 +36,11 @@ defmodule CodeCorps.GithubAppInstallation do def create_changeset(struct, params \\ %{}) do struct |> cast(params, [:project_id, :user_id]) - |> put_change(:state, "unprocessed") - |> put_change(:origin, "codecorps") |> validate_required([:project_id, :user_id]) |> assoc_constraint(:project) |> assoc_constraint(:user) + |> put_change(:origin, "codecorps") + |> put_change(:state, "unprocessed") end @doc ~S""" diff --git a/lib/code_corps/model/github_pull_request.ex b/lib/code_corps/model/github_pull_request.ex new file mode 100644 index 000000000..d69306ba5 --- /dev/null +++ b/lib/code_corps/model/github_pull_request.ex @@ -0,0 +1,73 @@ +defmodule CodeCorps.GithubPullRequest do + use Ecto.Schema + import Ecto.Changeset + + schema "github_pull_requests" do + field :additions, :integer + field :body, :string + field :changed_files, :integer + field :closed_at, :utc_datetime + field :comments, :integer + field :comments_url, :string + field :commits, :integer + field :commits_url, :string + field :deletions, :integer + field :diff_url, :string + field :github_created_at, :utc_datetime + field :github_id, :integer + field :github_updated_at, :utc_datetime + field :html_url, :string + field :issue_url, :string + field :locked, :boolean, default: false + field :merge_commit_sha, :string + field :mergeable_state, :string + field :merged, :boolean, default: false + field :merged_at, :utc_datetime + field :number, :integer + field :patch_url, :string + field :review_comment_url, :string + field :review_comments, :integer + field :review_comments_url, :string + field :state, :string + field :statuses_url, :string + field :title, :string + field :url, :string + + belongs_to :github_repo, CodeCorps.GithubRepo + + timestamps() + end + + @attrs [ + :additions, :body, :changed_files, :closed_at, :comments, :comments_url, + :commits, :commits_url, :deletions, :diff_url, :github_created_at, + :github_id, :github_updated_at, :html_url, :issue_url, :locked, + :merge_commit_sha, :mergeable_state, :merged, :merged_at, :number, + :patch_url, :review_comment_url, :review_comments, :review_comments_url, + :state, :statuses_url, :title, :url + ] + + @required_attrs [ + :github_created_at, :github_id, :github_updated_at, :html_url, :locked, + :merged, :number, :state, :title + ] + + @doc false + def changeset(struct, params) do + struct + |> cast(params, @attrs) + |> validate_required(@required_attrs) + end + + def create_changeset(struct, params) do + struct + |> changeset(params) + |> cast(params, [:github_repo_id]) + |> assoc_constraint(:github_repo) + end + + def update_changeset(struct, params) do + struct + |> changeset(params) + end +end diff --git a/lib/code_corps/model/task.ex b/lib/code_corps/model/task.ex index 231156c50..a7999ac09 100644 --- a/lib/code_corps/model/task.ex +++ b/lib/code_corps/model/task.ex @@ -25,6 +25,7 @@ defmodule CodeCorps.Task do field :position, :integer, virtual: true belongs_to :github_issue, CodeCorps.GithubIssue + belongs_to :github_pull_request, CodeCorps.GithubPullRequest belongs_to :github_repo, CodeCorps.GithubRepo belongs_to :project, CodeCorps.Project belongs_to :task_list, CodeCorps.TaskList diff --git a/lib/code_corps_web/controllers/github_pull_request_controller.ex b/lib/code_corps_web/controllers/github_pull_request_controller.ex new file mode 100644 index 000000000..fbe986109 --- /dev/null +++ b/lib/code_corps_web/controllers/github_pull_request_controller.ex @@ -0,0 +1,23 @@ +defmodule CodeCorpsWeb.GithubPullRequestController do + use CodeCorpsWeb, :controller + + alias CodeCorps.{GithubPullRequest, Helpers.Query} + + action_fallback CodeCorpsWeb.FallbackController + plug CodeCorpsWeb.Plug.DataToAttributes + plug CodeCorpsWeb.Plug.IdsToIntegers + + @spec index(Conn.t, map) :: Conn.t + def index(%Conn{} = conn, %{} = params) do + with github_pull_requests <- GithubPullRequest |> Query.id_filter(params) |> Repo.all do + conn |> render("index.json-api", data: github_pull_requests) + end + end + + @spec show(Conn.t, map) :: Conn.t + def show(%Conn{} = conn, %{"id" => id}) do + with %GithubPullRequest{} = github_pull_request <- GithubPullRequest |> Repo.get(id) do + conn |> render("show.json-api", data: github_pull_request) + end + end +end diff --git a/lib/code_corps_web/router.ex b/lib/code_corps_web/router.ex index 6ef231f09..8484ddf5f 100644 --- a/lib/code_corps_web/router.ex +++ b/lib/code_corps_web/router.ex @@ -108,6 +108,7 @@ defmodule CodeCorpsWeb.Router do resources "/donation-goals", DonationGoalController, only: [:index, :show] resources "/github-app-installations", GithubAppInstallationController, only: [:index, :show] resources "/github-issues", GithubIssueController, only: [:index, :show] + resources "/github-pull-requests", GithubPullRequestController, only: [:index, :show] resources "/github-repos", GithubRepoController, only: [:index, :show] resources "/organization-github-app-installations", OrganizationGithubAppInstallationController, only: [:index, :show] resources "/organizations", OrganizationController, only: [:index, :show] diff --git a/lib/code_corps_web/views/github_pull_request_view.ex b/lib/code_corps_web/views/github_pull_request_view.ex new file mode 100644 index 000000000..bd68c8a8e --- /dev/null +++ b/lib/code_corps_web/views/github_pull_request_view.ex @@ -0,0 +1,9 @@ +defmodule CodeCorpsWeb.GithubPullRequestView do + use CodeCorpsWeb.PreloadHelpers, default_preloads: [:github_repo] + use CodeCorpsWeb, :view + use JaSerializer.PhoenixView + + attributes [:github_created_at, :github_updated_at, :html_url, :merged, :number, :state] + + has_one :github_repo, serializer: CodeCorpsWeb.GithubRepoView +end diff --git a/lib/code_corps_web/views/task_view.ex b/lib/code_corps_web/views/task_view.ex index cfcf38d26..81529d76f 100644 --- a/lib/code_corps_web/views/task_view.ex +++ b/lib/code_corps_web/views/task_view.ex @@ -1,6 +1,6 @@ defmodule CodeCorpsWeb.TaskView do use CodeCorpsWeb.PreloadHelpers, - default_preloads: ~w(github_issue github_repo project user task_list task_skills comments user_task)a + default_preloads: ~w(github_issue github_repo github_pull_request project user task_list task_skills comments user_task)a use CodeCorpsWeb, :view use JaSerializer.PhoenixView @@ -10,6 +10,7 @@ defmodule CodeCorpsWeb.TaskView do ] has_one :github_issue, serializer: CodeCorpsWeb.GithubIssueView + has_one :github_pull_request, serializer: CodeCorpsWeb.GithubPullRequestView has_one :github_repo, serializer: CodeCorpsWeb.GithubRepoView has_one :project, serializer: CodeCorpsWeb.ProjectView has_one :task_list, serializer: CodeCorpsWeb.TaskListView diff --git a/priv/repo/migrations/20171016223356_create_github_pull_requests.exs b/priv/repo/migrations/20171016223356_create_github_pull_requests.exs new file mode 100644 index 000000000..8e4aea98f --- /dev/null +++ b/priv/repo/migrations/20171016223356_create_github_pull_requests.exs @@ -0,0 +1,43 @@ +defmodule CodeCorps.Repo.Migrations.CreateGithubPullRequests do + use Ecto.Migration + + def change do + create table(:github_pull_requests) do + add :additions, :integer + add :body, :text + add :changed_files, :integer + add :closed_at, :utc_datetime + add :comments, :integer + add :comments_url, :text + add :commits, :integer + add :commits_url, :text + add :deletions, :integer + add :diff_url, :text + add :github_created_at, :utc_datetime + add :github_id, :integer + add :github_updated_at, :utc_datetime + add :html_url, :text + add :issue_url, :text + add :locked, :boolean, default: false, null: false + add :merge_commit_sha, :text + add :mergeable_state, :text + add :merged, :boolean, default: false, null: false + add :merged_at, :utc_datetime + add :number, :integer + add :patch_url, :text + add :review_comment_url, :text + add :review_comments, :integer + add :review_comments_url, :text + add :state, :string + add :statuses_url, :text + add :title, :text + add :url, :text + + timestamps() + + add :github_repo_id, references(:github_repos) + end + + create unique_index(:github_pull_requests, [:github_id]) + end +end diff --git a/priv/repo/migrations/20171016235656_add_pull_requests_to_tasks.exs b/priv/repo/migrations/20171016235656_add_pull_requests_to_tasks.exs new file mode 100644 index 000000000..10a04be26 --- /dev/null +++ b/priv/repo/migrations/20171016235656_add_pull_requests_to_tasks.exs @@ -0,0 +1,15 @@ +defmodule CodeCorps.Repo.Migrations.AddPullRequestsToTasks do + use Ecto.Migration + + def up do + alter table(:tasks) do + add :github_pull_request_id, references(:github_pull_requests) + end + end + + def down do + alter table(:tasks) do + remove :github_pull_request_id + end + end +end diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index e26edfe61..a439cc4c9 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -350,6 +350,66 @@ CREATE SEQUENCE github_issues_id_seq ALTER SEQUENCE github_issues_id_seq OWNED BY github_issues.id; +-- +-- Name: github_pull_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE github_pull_requests ( + id bigint NOT NULL, + additions integer, + body text, + changed_files integer, + closed_at timestamp without time zone, + comments integer, + comments_url text, + commits integer, + commits_url text, + deletions integer, + diff_url text, + github_created_at timestamp without time zone, + github_id integer, + github_updated_at timestamp without time zone, + html_url text, + issue_url text, + locked boolean DEFAULT false NOT NULL, + merge_commit_sha text, + mergeable_state text, + merged boolean DEFAULT false NOT NULL, + merged_at timestamp without time zone, + number integer, + patch_url text, + review_comment_url text, + review_comments integer, + review_comments_url text, + state character varying(255), + statuses_url text, + title text, + url text, + inserted_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + github_repo_id bigint +); + + +-- +-- Name: github_pull_requests_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE github_pull_requests_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: github_pull_requests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE github_pull_requests_id_seq OWNED BY github_pull_requests.id; + + -- -- Name: github_repos; Type: TABLE; Schema: public; Owner: - -- @@ -1460,7 +1520,8 @@ CREATE TABLE tasks ( created_from character varying(255) DEFAULT 'code_corps'::character varying, modified_from character varying(255) DEFAULT 'code_corps'::character varying, archived boolean DEFAULT false NOT NULL, - github_issue_id bigint + github_issue_id bigint, + github_pull_request_id bigint ); @@ -1716,6 +1777,13 @@ ALTER TABLE ONLY github_events ALTER COLUMN id SET DEFAULT nextval('github_event ALTER TABLE ONLY github_issues ALTER COLUMN id SET DEFAULT nextval('github_issues_id_seq'::regclass); +-- +-- Name: github_pull_requests id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY github_pull_requests ALTER COLUMN id SET DEFAULT nextval('github_pull_requests_id_seq'::regclass); + + -- -- Name: github_repos id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2018,6 +2086,14 @@ ALTER TABLE ONLY github_issues ADD CONSTRAINT github_issues_pkey PRIMARY KEY (id); +-- +-- Name: github_pull_requests github_pull_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY github_pull_requests + ADD CONSTRAINT github_pull_requests_pkey PRIMARY KEY (id); + + -- -- Name: github_repos github_repos_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2306,6 +2382,13 @@ CREATE INDEX github_app_installations_project_id_index ON github_app_installatio CREATE INDEX github_app_installations_user_id_index ON github_app_installations USING btree (user_id); +-- +-- Name: github_pull_requests_github_id_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX github_pull_requests_github_id_index ON github_pull_requests USING btree (github_id); + + -- -- Name: github_repos_github_app_installation_id_index; Type: INDEX; Schema: public; Owner: - -- @@ -2777,6 +2860,14 @@ ALTER TABLE ONLY github_issues ADD CONSTRAINT github_issues_github_repo_id_fkey FOREIGN KEY (github_repo_id) REFERENCES github_repos(id); +-- +-- Name: github_pull_requests github_pull_requests_github_repo_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY github_pull_requests + ADD CONSTRAINT github_pull_requests_github_repo_id_fkey FOREIGN KEY (github_repo_id) REFERENCES github_repos(id); + + -- -- Name: github_repos github_repos_github_app_installation_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3097,6 +3188,14 @@ ALTER TABLE ONLY tasks ADD CONSTRAINT tasks_github_issue_id_fkey FOREIGN KEY (github_issue_id) REFERENCES github_issues(id); +-- +-- Name: tasks tasks_github_pull_request_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY tasks + ADD CONSTRAINT tasks_github_pull_request_id_fkey FOREIGN KEY (github_pull_request_id) REFERENCES github_pull_requests(id); + + -- -- Name: tasks tasks_github_repo_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3197,5 +3296,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), (20170505224222), (20170526095401), (20170602000208), (20170622205732), (20170626231059), (20170628092119), (20170628213609), (20170629183404), (20170630140136), (20170706132431), (20170707213648), (20170711122252), (20170717092127), (20170725060612), (20170727052644), (20170731130121), (20170814131722), (20170913114958), (20170921014405), (20170925214512), (20170925230419), (20170926134646), (20170927100300), (20170928234412), (20171003134956), (20171003225853), (20171006063358), (20171006161407), (20171012215106), (20171012221231), (20171016125229), (20171016125516); +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), (20170505224222), (20170526095401), (20170602000208), (20170622205732), (20170626231059), (20170628092119), (20170628213609), (20170629183404), (20170630140136), (20170706132431), (20170707213648), (20170711122252), (20170717092127), (20170725060612), (20170727052644), (20170731130121), (20170814131722), (20170913114958), (20170921014405), (20170925214512), (20170925230419), (20170926134646), (20170927100300), (20170928234412), (20171003134956), (20171003225853), (20171006063358), (20171006161407), (20171012215106), (20171012221231), (20171016125229), (20171016125516), (20171016223356), (20171016235656); diff --git a/test/fixtures/github/events/pull_request_closed.json b/test/fixtures/github/events/pull_request_closed.json new file mode 100644 index 000000000..f9893f477 --- /dev/null +++ b/test/fixtures/github/events/pull_request_closed.json @@ -0,0 +1,415 @@ +{ + "action": "closed", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "closed", + "locked": false, + "title": "Update the README with new information", + "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": "User", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:40:27Z", + "closed_at": "2075-05-05T23:45:49Z", + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "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 + }, + "installation": { + "id": 234 + } +} diff --git a/test/fixtures/github/events/pull_request_edited.json b/test/fixtures/github/events/pull_request_edited.json new file mode 100644 index 000000000..3ab42497e --- /dev/null +++ b/test/fixtures/github/events/pull_request_edited.json @@ -0,0 +1,415 @@ +{ + "action": "edited", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "open", + "locked": false, + "title": "Update the README with new information", + "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": "User", + "site_admin": false + }, + "body": "This is a pretty easy change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:43:41Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "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 + }, + "installation": { + "id": 234 + } +} diff --git a/test/fixtures/github/events/pull_request_opened.json b/test/fixtures/github/events/pull_request_opened.json new file mode 100644 index 000000000..e29280d36 --- /dev/null +++ b/test/fixtures/github/events/pull_request_opened.json @@ -0,0 +1,415 @@ +{ + "action": "opened", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "open", + "locked": false, + "title": "Update the README with new information", + "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": "User", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:40:27Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "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 + }, + "installation": { + "id": 234 + } +} diff --git a/test/fixtures/github/events/pull_request_opened_by_bot.json b/test/fixtures/github/events/pull_request_opened_by_bot.json new file mode 100644 index 000000000..ce98d19a7 --- /dev/null +++ b/test/fixtures/github/events/pull_request_opened_by_bot.json @@ -0,0 +1,415 @@ +{ + "action": "opened", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "open", + "locked": false, + "title": "Update the README with new information", + "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 + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:40:27Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "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 + }, + "installation": { + "id": 234 + } +} diff --git a/test/fixtures/github/events/pull_request_reopened.json b/test/fixtures/github/events/pull_request_reopened.json new file mode 100644 index 000000000..486bb23f8 --- /dev/null +++ b/test/fixtures/github/events/pull_request_reopened.json @@ -0,0 +1,415 @@ +{ + "action": "reopened", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "open", + "locked": false, + "title": "Update the README with new information", + "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": "User", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:40:27Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "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": "User", + "site_admin": false + }, + "repo": { + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "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": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "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": 1, + "forks": 0, + "open_issues": 1, + "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 + }, + "installation": { + "id": 234 + } +} diff --git a/test/lib/code_corps/github/adapters/github_app_installation_test.exs b/test/lib/code_corps/github/adapters/app_installation_test.exs similarity index 77% rename from test/lib/code_corps/github/adapters/github_app_installation_test.exs rename to test/lib/code_corps/github/adapters/app_installation_test.exs index 45d14e442..3ed9086bd 100644 --- a/test/lib/code_corps/github/adapters/github_app_installation_test.exs +++ b/test/lib/code_corps/github/adapters/app_installation_test.exs @@ -1,17 +1,17 @@ -defmodule CodeCorps.GitHub.Adapters.GithubAppInstallationTest do +defmodule CodeCorps.GitHub.Adapters.AppInstallationTest do @moduledoc false use ExUnit.Case, async: true import CodeCorps.GitHub.TestHelpers - alias CodeCorps.GitHub.Adapters.GithubAppInstallation + alias CodeCorps.GitHub.Adapters.AppInstallation describe "from_installation_event/1" do test "maps api payload correctly" do payload = load_event_fixture("installation_created") - assert GithubAppInstallation.from_installation_event(payload) == %{ + assert AppInstallation.from_installation_event(payload) == %{ github_id: payload["installation"]["id"], github_account_id: payload["installation"]["account"]["id"], github_account_login: payload["installation"]["account"]["login"], diff --git a/test/lib/code_corps/github/adapters/pull_request_test.exs b/test/lib/code_corps/github/adapters/pull_request_test.exs new file mode 100644 index 000000000..0d4eff135 --- /dev/null +++ b/test/lib/code_corps/github/adapters/pull_request_test.exs @@ -0,0 +1,47 @@ +defmodule CodeCorps.GitHub.Adapters.PullRequestTest do + @moduledoc false + + use ExUnit.Case, async: true + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.GitHub.Adapters.PullRequest + + describe "from_api/1" do + test "maps api payload correctly" do + %{"pull_request" => payload} = load_event_fixture("pull_request_opened") + + assert PullRequest.from_api(payload) == %{ + additions: payload["additions"], + body: payload["body"], + changed_files: payload["changed_files"], + closed_at: payload["closed_at"], + comments: payload["comments"], + comments_url: payload["comments_url"], + commits: payload["commits"], + commits_url: payload["commits_url"], + deletions: payload["deletions"], + diff_url: payload["diff_url"], + github_created_at: payload["created_at"], + github_id: payload["id"], + github_updated_at: payload["updated_at"], + html_url: payload["html_url"], + issue_url: payload["issue_url"], + locked: payload["locked"], + merge_commit_sha: payload["merge_commit_sha"], + mergeable_state: payload["mergeable_state"], + merged: payload["merged"], + merged_at: payload["merged_at"], + number: payload["number"], + patch_url: payload["patch_url"], + review_comment_url: payload["review_comment_url"], + review_comments: payload["review_comments"], + review_comments_url: payload["review_comments_url"], + state: payload["state"], + statuses_url: payload["statuses_url"], + title: payload["title"], + url: payload["url"] + } + end + end +end diff --git a/test/lib/code_corps/github/adapters/github_repo_test.exs b/test/lib/code_corps/github/adapters/repo_test.exs similarity index 80% rename from test/lib/code_corps/github/adapters/github_repo_test.exs rename to test/lib/code_corps/github/adapters/repo_test.exs index 0f4a8fd88..3db47450c 100644 --- a/test/lib/code_corps/github/adapters/github_repo_test.exs +++ b/test/lib/code_corps/github/adapters/repo_test.exs @@ -1,17 +1,17 @@ -defmodule CodeCorps.GitHub.Adapters.GithubRepoTest do +defmodule CodeCorps.GitHub.Adapters.RepoTest do @moduledoc false use ExUnit.Case, async: true import CodeCorps.GitHub.TestHelpers - alias CodeCorps.GitHub.Adapters.GithubRepo + alias CodeCorps.GitHub.Adapters.Repo describe "from_api/1" do test "maps api payload correctly" do %{"repositories" => [repo]} = load_event_fixture("user_repositories") - assert GithubRepo.from_api(repo) == %{ + assert Repo.from_api(repo) == %{ github_id: repo |> get_in(["id"]), name: repo |> get_in(["name"]), github_account_id: repo |> get_in(["owner", "id"]), diff --git a/test/lib/code_corps/github/event/installation/repos_test.exs b/test/lib/code_corps/github/event/installation/repos_test.exs index 1ad88d121..54ea17c04 100644 --- a/test/lib/code_corps/github/event/installation/repos_test.exs +++ b/test/lib/code_corps/github/event/installation/repos_test.exs @@ -12,7 +12,7 @@ defmodule CodeCorps.GitHub.Event.Installation.ReposTest do Repo } - alias CodeCorps.GitHub.Adapters.GithubRepo, as: GithubRepoAdapter + alias CodeCorps.GitHub.Adapters.Repo, as: RepoAdapter # from fixture @installation_repositories load_endpoint_fixture("installation_repositories") @@ -40,8 +40,8 @@ defmodule CodeCorps.GitHub.Event.Installation.ReposTest do installation = insert(:github_app_installation, github_id: @app_github_id, state: "initiated_on_code_corps") %{"repositories" => [matched_repo_payload, new_repo_payload]} = @installation_repositories - matched_repo_attrs = matched_repo_payload |> GithubRepoAdapter.from_api - new_repo_attrs = new_repo_payload |> GithubRepoAdapter.from_api + matched_repo_attrs = matched_repo_payload |> RepoAdapter.from_api + new_repo_attrs = new_repo_payload |> RepoAdapter.from_api unmatched_repo = insert(:github_repo, github_app_installation: installation) _matched_repo = insert(:github_repo, matched_repo_attrs |> Map.put(:github_app_installation, installation)) diff --git a/test/lib/code_corps/github/event/pull_request/changeset_builder_test.exs b/test/lib/code_corps/github/event/pull_request/changeset_builder_test.exs new file mode 100644 index 000000000..387ce740c --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/changeset_builder_test.exs @@ -0,0 +1,82 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.ChangesetBuilderTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + import Ecto.Changeset + + alias CodeCorps.{ + GitHub.Event.PullRequest.ChangesetBuilder, + Task + } + + describe "build_changeset/3" do + test "assigns proper changes to the task" do + payload = load_event_fixture("pull_request_opened") + task = %Task{} + github_pull_request = insert(:github_pull_request) + project = insert(:project) + project_github_repo = insert(:project_github_repo, project: project) + user = insert(:user) + task_list = insert(:task_list, project: project, inbox: true) + + changeset = ChangesetBuilder.build_changeset( + task, payload, github_pull_request, project_github_repo, user + ) + + {:ok, created_at, _} = payload["pull_request"]["created_at"] |> DateTime.from_iso8601() + {:ok, updated_at, _} = payload["pull_request"]["updated_at"] |> DateTime.from_iso8601() + + # adapted fields + assert get_change(changeset, :created_at) == created_at + assert get_change(changeset, :markdown) == payload["pull_request"]["body"] + assert get_change(changeset, :modified_at) == updated_at + assert get_change(changeset, :name) == payload["pull_request"]["name"] + assert get_field(changeset, :status) == payload["pull_request"]["state"] + + # manual fields + assert get_change(changeset, :created_from) == "github" + assert get_change(changeset, :modified_from) == "github" + + # markdown was rendered into html + assert get_change(changeset, :body) == + payload["pull_request"]["body"] + |> Earmark.as_html!(%Earmark.Options{code_class_prefix: "language-"}) + + # relationships are proper + assert get_change(changeset, :github_pull_request_id) == github_pull_request.id + assert get_change(changeset, :github_repo_id) == project_github_repo.github_repo_id + assert get_change(changeset, :project_id) == project_github_repo.project_id + assert get_change(changeset, :task_list_id) == task_list.id + assert get_change(changeset, :user_id) == user.id + + assert changeset.valid? + end + + test "validates that modified_at has not already happened" do + payload = load_event_fixture("pull_request_opened") + %{"pull_request" => %{"updated_at" => updated_at}} = payload + + # Set the modified_at in the future + modified_at = + updated_at + |> Timex.parse!("{ISO:Extended:Z}") + |> Timex.shift(days: 1) + + project = insert(:project) + github_pull_request = insert(:github_pull_request) + github_repo = insert(:github_repo) + project_github_repo = insert(:project_github_repo, github_repo: github_repo, project: project) + user = insert(:user) + task = insert(:task, project: project, github_pull_request: github_pull_request, github_repo: github_repo, user: user, modified_at: modified_at) + + changeset = ChangesetBuilder.build_changeset( + task, payload, github_pull_request, project_github_repo, user + ) + + refute changeset.valid? + assert changeset.errors[:modified_at] == {"cannot be before the last recorded time", []} + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/pull_request_linker_test.exs b/test/lib/code_corps/github/event/pull_request/pull_request_linker_test.exs new file mode 100644 index 000000000..024a5fc89 --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/pull_request_linker_test.exs @@ -0,0 +1,58 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.PullRequestLinkerTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.{ + GithubPullRequest, + GitHub.Event.PullRequest.PullRequestLinker, + Repo + } + + alias CodeCorps.GitHub.Adapters.PullRequest, as: PullRequestAdapter + + @payload load_event_fixture("pull_request_opened") + + describe "create_or_update_pull_request/1" do + test "creates pull request if none exists" do + %{"pull_request" => attrs} = @payload + github_repo = insert(:github_repo) + {:ok, %GithubPullRequest{} = created_pull_request} = PullRequestLinker.create_or_update_pull_request(github_repo, attrs) + + assert Repo.one(GithubPullRequest) + + created_attributes = + attrs + |> PullRequestAdapter.from_api + |> Map.delete(:closed_at) + |> Map.delete(:merge_commit_sha) + |> Map.delete(:merged_at) + returned_pull_request = Repo.get_by(GithubPullRequest, created_attributes) + assert returned_pull_request.id == created_pull_request.id + assert returned_pull_request.github_repo_id == github_repo.id + end + + test "updates pull request if it already exists" do + %{"pull_request" => %{"id" => pull_request_id} = attrs} = @payload + + github_repo = insert(:github_repo) + pull_request = insert(:github_pull_request, github_id: pull_request_id, github_repo: github_repo) + + {:ok, %GithubPullRequest{} = updated_pull_request} = PullRequestLinker.create_or_update_pull_request(github_repo, attrs) + + assert updated_pull_request.id == pull_request.id + assert updated_pull_request.github_repo_id == github_repo.id + end + + test "returns changeset if payload is somehow not as expected" do + bad_payload = @payload |> put_in(["pull_request", "number"], nil) + %{"pull_request" => attrs} = bad_payload + github_repo = insert(:github_repo) + + {:error, changeset} = PullRequestLinker.create_or_update_pull_request(github_repo, attrs) + refute changeset.valid? + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/task_syncer_test.exs b/test/lib/code_corps/github/event/pull_request/task_syncer_test.exs new file mode 100644 index 000000000..596ff7278 --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/task_syncer_test.exs @@ -0,0 +1,69 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.TaskSyncerTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.{ + GitHub.Event.PullRequest.TaskSyncer, + Project, + Repo, + Task + } + + describe "sync all/3" do + @payload load_event_fixture("pull_request_opened") + + test "creates missing, updates existing tasks for each project associated with the github repo" do + user = insert(:user) + %{github_repo: github_repo} = github_pull_request = insert(:github_pull_request) + + %{"pull_request" => %{"body" => pull_request_body}} = @payload + + [%{project: project_1}, _, _] = project_github_repos = + insert_list(3, :project_github_repo, github_repo: github_repo) + + task_1 = insert(:task, project: project_1, github_pull_request: github_pull_request, github_repo: github_repo, user: user) + + project_ids = project_github_repos |> Enum.map(&Map.get(&1, :project_id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = github_pull_request |> TaskSyncer.sync_all(user, @payload) + + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + assert task.user_id == user.id + assert task.markdown == pull_request_body + assert task.github_pull_request_id == github_pull_request.id + end) + + task_ids = tasks |> Enum.map(&Map.get(&1, :id)) + assert task_1.id in task_ids + end + + test "fails on validation errors" do + %{github_repo: github_repo} = github_pull_request = insert(:github_pull_request) + + bad_payload = @payload |> put_in(~w(pull_request title), nil) + + %{project: project} = + insert(:project_github_repo, github_repo: github_repo) + + %{user: user} = insert(:task, project: project, github_pull_request: github_pull_request, github_repo: github_repo) + + insert(:task_list, project: project, inbox: true) + + {:error, {tasks, errors}} = + github_pull_request |> TaskSyncer.sync_all(user, bad_payload) + + assert tasks |> Enum.count == 0 + assert errors |> Enum.count == 1 + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/user_linker_test.exs b/test/lib/code_corps/github/event/pull_request/user_linker_test.exs new file mode 100644 index 000000000..c5eaa8af2 --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/user_linker_test.exs @@ -0,0 +1,119 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.UserLinkerTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.{ + GitHub.Event.PullRequest.UserLinker, + Repo, + User + } + + alias CodeCorps.GitHub.Adapters.User, as: UserAdapter + + @payload load_event_fixture("pull_request_opened") + @bot_payload load_event_fixture("pull_request_opened_by_bot") + @user_payload @payload["pull_request"]["user"] + @bot_user_payload @bot_payload["pull_request"]["user"] + + describe "find_or_create_user/1" do + test "finds user by task association" do + %{ + "pull_request" => %{"number" => number}, + "repository" => %{"id" => github_repo_id} + } = @payload + + user = insert(:user) + github_repo = insert(:github_repo, github_id: github_repo_id) + github_pull_request = insert(:github_pull_request, number: number, github_repo: github_repo) + # multiple tasks, all with same user is ok + insert_pair( + :task, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(github_pull_request, @payload) + + assert user.id == returned_user.id + end + + test "returns error if multiple users by task association found" do + %{ + "pull_request" => %{"number" => number}, + "repository" => %{"id" => github_repo_id} + } = @payload + + github_repo = insert(:github_repo, github_id: github_repo_id) + github_pull_request = insert(:github_pull_request, number: number, github_repo: github_repo) + # multiple tasks, each with different user is not ok + insert_pair(:task, github_repo: github_repo, github_pull_request: github_pull_request) + + assert {:error, :multiple_users} == + UserLinker.find_or_create_user(github_pull_request, @payload) + end + + test "returns user by github id if no user by task association found" do + %{"pull_request" => %{"number" => number}} = @payload + attributes = UserAdapter.from_github_user(@user_payload) + preinserted_user = insert(:user, attributes) + github_pull_request = insert(:github_pull_request, number: number) + + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(github_pull_request, @payload) + + assert preinserted_user.id == returned_user.id + assert Repo.get_by(User, attributes) + end + + test "creates user if none is found by any other method" do + %{"pull_request" => %{"number" => number}} = @payload + github_pull_request = insert(:github_pull_request, number: number) + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(github_pull_request, @payload) + + created_attributes = UserAdapter.from_github_user(@user_payload) + created_user = Repo.get_by(User, created_attributes) + assert created_user.id == returned_user.id + end + + test "if pull request opened by bot, finds user by task association" do + %{ + "pull_request" => %{ + "number" => number, "user" => %{"id" => bot_user_github_id}}, + "repository" => %{"id" => github_repo_id} + } = @bot_payload + + preinserted_user = insert(:user) + github_pull_request = insert(:github_pull_request, number: number) + repo = insert(:github_repo, github_id: github_repo_id) + insert( + :task, + user: preinserted_user, github_repo: repo, + github_pull_request: github_pull_request) + + {:ok, %User{} = returned_user} = + UserLinker.find_or_create_user(github_pull_request, @bot_payload) + + assert preinserted_user.id == returned_user.id + + refute Repo.get_by(User, github_id: bot_user_github_id) + end + + test "if pull request opened by bot, and no user by task association, creates a bot user" do + %{"pull_request" => %{"number" => number}} = @bot_payload + github_pull_request = insert(:github_pull_request, number: number) + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(github_pull_request, @bot_payload) + + 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 + %{"pull_request" => %{"number" => number}} = @payload + github_pull_request = insert(:github_pull_request, number: number) + bad_payload = @payload |> put_in(["pull_request", "user", "type"], "Organization") + + {:error, changeset} = UserLinker.find_or_create_user(github_pull_request, bad_payload) + refute changeset.valid? + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/validator_test.exs b/test/lib/code_corps/github/event/pull_request/validator_test.exs new file mode 100644 index 000000000..32338c26a --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/validator_test.exs @@ -0,0 +1,24 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.ValidatorTest do + @moduledoc false + + use ExUnit.Case, async: true + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.GitHub.Event.PullRequest.Validator + + describe "valid?/1" do + test "returns true for any PullRequest event fixture" do + assert "pull_request_opened" |> load_event_fixture() |> Validator.valid? + assert "pull_request_closed" |> load_event_fixture() |> Validator.valid? + assert "pull_request_edited" |> load_event_fixture() |> Validator.valid? + assert "pull_request_reopened" |> load_event_fixture() |> Validator.valid? + end + + test "returns false for an unsupported structure" do + refute Validator.valid?("foo") + refute Validator.valid?(%{"foo" => "bar"}) + refute Validator.valid?(%{"issue" => %{"bar" => "baz"}}) + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request_test.exs b/test/lib/code_corps/github/event/pull_request_test.exs new file mode 100644 index 000000000..54ced321c --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request_test.exs @@ -0,0 +1,513 @@ +defmodule CodeCorps.GitHub.Event.PullRequestTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.{ + GitHub.Event.PullRequest, + Project, + Repo, + Task, + User + } + + describe "handle/2" do + @payload load_event_fixture("pull_request_opened") |> Map.put("action", "foo") + + test "returns error if action of the event is wrong" do + assert {:error, :unexpected_action} == PullRequest.handle(@payload) + end + end + + describe "handle/2 for PullRequest::opened" do + @payload load_event_fixture("pull_request_opened") + + test "with unmatched user, creates user, creates task for each project associated to github repo" do + %{ + "pull_request" => %{ + "body" => markdown, "title" => title, "number" => number, + "user" => %{"id" => user_github_id} + }, + "repository" => %{"id" => repo_github_id} + } = @payload + + github_repo = insert(:github_repo, github_id: repo_github_id) + + project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + user = Repo.get_by(User, github_id: user_github_id) + assert user + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.user_id == user.id + assert task.github_pull_request_id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + end + + test "with unmatched user, returns error if unmatched repository" do + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + refute Repo.one(User) + end + + test "with matched user, creates or updates task for each project associated to github repo" do + %{ + "pull_request" => %{"id" => pull_request_github_id, "body" => markdown, "title" => title, "number" => number, "user" => %{"id" => user_github_id}}, + "repository" => %{"id" => repo_github_id} + } = @payload + + user = insert(:user, github_id: user_github_id) + + github_repo = insert(:github_repo, github_id: repo_github_id) + github_pull_request = insert(:github_pull_request, github_id: pull_request_github_id, github_repo: github_repo) + + [%{project: project} | _rest] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + %{id: existing_task_id} = + insert(:task, project: project, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.github_pull_request_id == github_pull_request.id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + + assert existing_task_id in (tasks |> Enum.map(&Map.get(&1, :id))) + end + + test "with matched user, returns error if unmatched repository" do + %{"pull_request" => %{"user" => %{"id" => user_github_id}}} = @payload + insert(:user, github_id: user_github_id) + + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + end + + test "returns error if payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(%{}) + end + + test "returns error if repo payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("repository", "foo")) + end + + test "returns error if pull request payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("pull_request", "foo")) + end + end + + describe "handle/2 for PullRequest::closed" do + @payload load_event_fixture("pull_request_closed") + + test "with unmatched user, creates user, creates task for each project associated to github repo" do + %{ + "pull_request" => %{ + "body" => markdown, "title" => title, "number" => number, + "user" => %{"id" => user_github_id} + }, + "repository" => %{"id" => repo_github_id} + } = @payload + + github_repo = insert(:github_repo, github_id: repo_github_id) + + project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + user = Repo.get_by(User, github_id: user_github_id) + assert user + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.user_id == user.id + assert task.github_pull_request_id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "closed" + end) + end + + test "with unmatched user, returns error if unmatched repository" do + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + refute Repo.one(User) + end + + test "with matched user, creates or updates task for each project associated to github repo" do + %{ + "pull_request" => %{"id" => pull_request_github_id, "body" => markdown, "title" => title, "number" => number, "user" => %{"id" => user_github_id}}, + "repository" => %{"id" => repo_github_id} + } = @payload + + user = insert(:user, github_id: user_github_id) + + github_repo = insert(:github_repo, github_id: repo_github_id) + github_pull_request = insert(:github_pull_request, github_id: pull_request_github_id, github_repo: github_repo) + + [%{project: project} | _rest] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + %{id: existing_task_id} = + insert(:task, project: project, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.github_pull_request_id == github_pull_request.id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "closed" + end) + + assert existing_task_id in (tasks |> Enum.map(&Map.get(&1, :id))) + end + + test "with matched user, returns error if unmatched repository" do + %{"pull_request" => %{"user" => %{"id" => user_github_id}}} = @payload + insert(:user, github_id: user_github_id) + + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + end + + test "returns error if payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(%{}) + end + + test "returns error if repo payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("repository", "foo")) + end + + test "returns error if pull request payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("pull_request", "foo")) + end + end + + describe "handle/2 for PullRequest::edited" do + @payload load_event_fixture("pull_request_edited") + + test "with unmatched user, creates user, creates task for each project associated to github repo" do + %{ + "pull_request" => %{ + "body" => markdown, "title" => title, "number" => number, + "user" => %{"id" => user_github_id} + }, + "repository" => %{"id" => repo_github_id} + } = @payload + + github_repo = insert(:github_repo, github_id: repo_github_id) + + project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + user = Repo.get_by(User, github_id: user_github_id) + assert user + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.user_id == user.id + assert task.github_pull_request_id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + end + + test "with unmatched user, returns error if unmatched repository" do + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + refute Repo.one(User) + end + + test "with matched user, creates or updates task for each project associated to github repo" do + %{ + "pull_request" => %{"id" => pull_request_github_id, "body" => markdown, "title" => title, "number" => number, "user" => %{"id" => user_github_id}}, + "repository" => %{"id" => repo_github_id} + } = @payload + + user = insert(:user, github_id: user_github_id) + + github_repo = insert(:github_repo, github_id: repo_github_id) + github_pull_request = insert(:github_pull_request, github_id: pull_request_github_id, github_repo: github_repo) + + [%{project: project} | _rest] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + %{id: existing_task_id} = + insert(:task, project: project, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.github_pull_request_id == github_pull_request.id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + + assert existing_task_id in (tasks |> Enum.map(&Map.get(&1, :id))) + end + + test "with matched user, returns error if unmatched repository" do + %{"pull_request" => %{"user" => %{"id" => user_github_id}}} = @payload + insert(:user, github_id: user_github_id) + + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + end + + test "returns error if payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(%{}) + end + + test "returns error if repo payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("repository", "foo")) + end + + test "returns error if pull request payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("pull_request", "foo")) + end + end + + describe "handle/2 for PullRequest::reopened" do + @payload load_event_fixture("pull_request_reopened") + + test "with unmatched user, creates user, creates task for each project associated to github repo" do + %{ + "pull_request" => %{ + "body" => markdown, "title" => title, "number" => number, + "user" => %{"id" => user_github_id} + }, + "repository" => %{"id" => repo_github_id} + } = @payload + + github_repo = insert(:github_repo, github_id: repo_github_id) + + project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + user = Repo.get_by(User, github_id: user_github_id) + assert user + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.user_id == user.id + assert task.github_pull_request_id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + end + + test "with unmatched user, returns error if unmatched repository" do + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + refute Repo.one(User) + end + + test "with matched user, creates or updates task for each project associated to github repo" do + %{ + "pull_request" => %{"id" => pull_request_github_id, "body" => markdown, "title" => title, "number" => number, "user" => %{"id" => user_github_id}}, + "repository" => %{"id" => repo_github_id} + } = @payload + + user = insert(:user, github_id: user_github_id) + + github_repo = insert(:github_repo, github_id: repo_github_id) + github_pull_request = insert(:github_pull_request, github_id: pull_request_github_id, github_repo: github_repo) + + [%{project: project} | _rest] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + %{id: existing_task_id} = + insert(:task, project: project, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.github_pull_request_id == github_pull_request.id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + + assert existing_task_id in (tasks |> Enum.map(&Map.get(&1, :id))) + end + + test "with matched user, returns error if unmatched repository" do + %{"pull_request" => %{"user" => %{"id" => user_github_id}}} = @payload + insert(:user, github_id: user_github_id) + + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + end + + test "returns error if payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(%{}) + end + + test "returns error if repo payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("repository", "foo")) + end + + test "returns error if pull request payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("pull_request", "foo")) + end + end + + @unimplemented_actions ~w( + assigned unassigned review_requested review_request_removed labeled + unlabeled + ) + + @unimplemented_actions |> Enum.each(fn action -> + describe "handle/2 for PullRequest::#{action}" do + @payload %{ + "action" => action, + "pull_request" => %{ + "id" => 1, "title" => "foo", "body" => "bar", "state" => "baz", + "user" => %{"id" => "bat"} + }, + "repository" => %{"id" => 2} + } + + test "is not implemented" do + assert PullRequest.handle(@payload) == {:error, :not_fully_implemented} + end + end + end) +end diff --git a/test/lib/code_corps/model/github_pull_request_test.exs b/test/lib/code_corps/model/github_pull_request_test.exs new file mode 100644 index 000000000..f2aa56402 --- /dev/null +++ b/test/lib/code_corps/model/github_pull_request_test.exs @@ -0,0 +1,31 @@ +defmodule CodeCorps.GithubPullRequestTest do + use CodeCorps.ModelCase + + alias CodeCorps.GithubPullRequest + + @valid_attrs %{ + github_created_at: "2075-05-05T23:40:27Z", + github_id: 34778301, + github_updated_at: "2075-05-05T23:40:27Z", + html_url: "https://github.com/baxterthehacker/public-repo/pull/1", + locked: false, + merged: false, + number: 1, + state: "open", + title: "Update the README with new information", + url: "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + } + @invalid_attrs %{} + + describe "create_changeset/2" do + test "with valid attributes" do + changeset = GithubPullRequest.create_changeset(%GithubPullRequest{}, @valid_attrs) + assert changeset.valid? + end + + test "with invalid attributes" do + changeset = GithubPullRequest.create_changeset(%GithubPullRequest{}, @invalid_attrs) + refute changeset.valid? + end + end +end diff --git a/test/lib/code_corps_web/controllers/github_pull_request_controller_test.exs b/test/lib/code_corps_web/controllers/github_pull_request_controller_test.exs new file mode 100644 index 000000000..afc71cc36 --- /dev/null +++ b/test/lib/code_corps_web/controllers/github_pull_request_controller_test.exs @@ -0,0 +1,39 @@ +defmodule CodeCorpsWeb.GithubPullRequestControllerTest do + use CodeCorpsWeb.ApiCase, resource_name: :github_pull_request + + describe "index" do + test "lists all resources", %{conn: conn} do + [record_1, record_2] = insert_pair(:github_pull_request) + + conn + |> request_index + |> json_response(200) + |> assert_ids_from_response([record_1.id, record_2.id]) + end + + test "filters resources by record id", %{conn: conn} do + [record_1, record_2 | _] = insert_list(3, :github_pull_request) + + path = "github-pull-requests/?filter[id]=#{record_1.id},#{record_2.id}" + + conn + |> get(path) + |> json_response(200) + |> assert_ids_from_response([record_1.id, record_2.id]) + end + end + + describe "show" do + test "shows chosen resource", %{conn: conn} do + record = insert(:github_pull_request) + conn + |> request_show(record) + |> json_response(200) + |> assert_id_from_response(record.id) + end + + test "renders 404 when id is nonexistent", %{conn: conn} do + assert conn |> request_show(:not_found) |> json_response(404) + end + end +end diff --git a/test/lib/code_corps_web/views/github_pull_request_view_test.exs b/test/lib/code_corps_web/views/github_pull_request_view_test.exs new file mode 100644 index 000000000..e89fb5167 --- /dev/null +++ b/test/lib/code_corps_web/views/github_pull_request_view_test.exs @@ -0,0 +1,35 @@ +defmodule CodeCorpsWeb.GithubPullRequestViewTest do + use CodeCorpsWeb.ViewCase + + test "renders all attributes and relationships properly" do + github_repo = insert(:github_repo) + github_pull_request = insert(:github_pull_request, github_repo: github_repo) + + rendered_json = render(CodeCorpsWeb.GithubPullRequestView, "show.json-api", data: github_pull_request) + + expected_json = %{ + "data" => %{ + "id" => github_pull_request.id |> Integer.to_string, + "type" => "github-pull-request", + "attributes" => %{ + "github-created-at" => github_pull_request.github_created_at, + "github-updated-at" => github_pull_request.github_updated_at, + "html-url" => github_pull_request.html_url, + "merged" => github_pull_request.merged, + "number" => github_pull_request.number, + "state" => github_pull_request.state + }, + "relationships" => %{ + "github-repo" => %{ + "data" => %{"id" => github_repo.id |> Integer.to_string, "type" => "github-repo"} + } + } + }, + "jsonapi" => %{ + "version" => "1.0" + } + } + + assert rendered_json == expected_json + end +end diff --git a/test/lib/code_corps_web/views/task_view_test.exs b/test/lib/code_corps_web/views/task_view_test.exs index eff733cd4..6686e2b77 100644 --- a/test/lib/code_corps_web/views/task_view_test.exs +++ b/test/lib/code_corps_web/views/task_view_test.exs @@ -3,8 +3,9 @@ defmodule CodeCorpsWeb.TaskViewTest do test "renders all attributes and relationships properly" do github_issue = insert(:github_issue) + github_pull_request = insert(:github_pull_request) github_repo = insert(:github_repo) - task = insert(:task, order: 1000, github_issue: github_issue, github_repo: github_repo) + task = insert(:task, order: 1000, github_issue: github_issue, github_pull_request: github_pull_request, github_repo: github_repo) comment = insert(:comment, task: task) task_skill = insert(:task_skill, task: task) user_task = insert(:user_task, task: task) @@ -50,6 +51,12 @@ defmodule CodeCorpsWeb.TaskViewTest do "type" => "github-issue" } }, + "github-pull-request" => %{ + "data" => %{ + "id" => task.github_pull_request_id |> Integer.to_string, + "type" => "github-pull-request" + } + }, "github-repo" => %{ "data" => %{ "id" => task.github_repo_id |> Integer.to_string, diff --git a/test/support/factories.ex b/test/support/factories.ex index 66bcbc7d6..919696f8a 100644 --- a/test/support/factories.ex +++ b/test/support/factories.ex @@ -67,6 +67,21 @@ defmodule CodeCorps.Factories do } end + def github_pull_request_factory do + %CodeCorps.GithubPullRequest{ + body: "Here's a change!", + github_created_at: DateTime.utc_now, + github_id: sequence(:id, (fn number -> number end)), + github_updated_at: DateTime.utc_now, + locked: false, + merged: false, + number: sequence(:id, (fn number -> number end)), + state: "open", + title: "Here's a change!", + github_repo: build(:github_repo) + } + end + def github_repo_factory do %CodeCorps.GithubRepo{ github_app_installation: build(:github_app_installation) From f8deb99953d6f9008dd6c9eb4e8e6e4deb174a5f Mon Sep 17 00:00:00 2001 From: Nikola Begedin Date: Tue, 17 Oct 2017 10:36:56 +0200 Subject: [PATCH 2/2] Added body parser --- .../github/event/pull_request/body_parser.ex | 25 +++++++++++++++ .../event/pull_request/body_parser_test.exs | 32 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 lib/code_corps/github/event/pull_request/body_parser.ex create mode 100644 test/lib/code_corps/github/event/pull_request/body_parser_test.exs diff --git a/lib/code_corps/github/event/pull_request/body_parser.ex b/lib/code_corps/github/event/pull_request/body_parser.ex new file mode 100644 index 000000000..20bd882ba --- /dev/null +++ b/lib/code_corps/github/event/pull_request/body_parser.ex @@ -0,0 +1,25 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.BodyParser do + @moduledoc ~S""" + In charge of extracting ids from markdown content, paired to a predefined list + of keywords. + """ + + @doc ~S""" + Searchs for GitHub closing keyword format inside a content string. Returns all + unique ids matched, as integers. + """ + @spec extract_closing_ids(String.t) :: list(integer) + def extract_closing_ids(content) when is_binary(content) do + ~w(close closes closed fix fixes fixed resolve resolves resolved) + |> matching_regex() + |> Regex.scan(content) # [["closes #1", "closes", "1"], ["fixes #2", "fixes", "2"]] + |> Enum.map(&List.last/1) # ["1", "2"] + |> Enum.map(&String.to_integer/1) # [1, 2] + |> Enum.uniq + end + + defp matching_regex(keywords) do + matches = keywords |> Enum.join("|") + ~r/(?:(#{matches}))\s+#(\d+)/i + end +end diff --git a/test/lib/code_corps/github/event/pull_request/body_parser_test.exs b/test/lib/code_corps/github/event/pull_request/body_parser_test.exs new file mode 100644 index 000000000..a265fbeed --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/body_parser_test.exs @@ -0,0 +1,32 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.BodyParserTest do + @moduledoc false + + use ExUnit.Case, async: true + + + alias CodeCorps.{ + GitHub.Event.PullRequest.BodyParser + } + + describe "extract_closing_ids/1" do + test "correctly extracts ids using supported closing keywords" do + content = + """ + close #2, closes #3 closed #4: fixed #5 fixes #6 fix #7. + resolve #8 resolves #9 #resolved #10 + """ + + assert content |> BodyParser.extract_closing_ids == 2..10 |> Enum.to_list + end + + test "only returns unique results" do + content = + """ + close #2, closes #2 closed #3: fixed #4 fixes #5 fix #6. + resolve #7 resolves #8 #resolved #8 + """ + + assert content |> BodyParser.extract_closing_ids == 2..8 |> Enum.to_list + end + end +end