-
Notifications
You must be signed in to change notification settings - Fork 86
Issues webhook handling #848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Yes, we should be creating tasks if they're not found. Ideally, of course, we have some mechanism to prevent actually ingesting these events if the sync is still being performed. Bear in mind that a particularly large repository may take several hours of processing time. Even our own repositories are at this stage. We should probably design with this expectation in mind. Luckily we have good test cases available to us in order to get this right.
We create a user which can later be merged with a new user or a newly connected-to-GitHub user. There are some tasks hanging around related to this, including the one to remove the database-level reliance on a user's email being present when signing up (GitHub does not necessarily provide us with this information; more often than not, it will not). |
571a18e to
7021416
Compare
| @@ -0,0 +1,86 @@ | |||
| defmodule CodeCorps.Adapter.MapTransformer do | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We were using this module as a Stripe util module, but the approach it uses is generic and can be reused for the purposes of GitHub adapters, so I moved it to outside of Stripe's scope.
I also did some renaming and added documentation to make it clearer what the functions in it actually do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tested this still works with Stripe locally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some testing, but generally, the stripe adapter layer is already well-tested.
I went through it one more time and the only thing that was missing was a from_params_test for the StripeConnectAccount adapter, so I added that one.
| |> Changeset.validate_required([ | ||
| :github_id, :name, :github_account_id, | ||
| :github_account_avatar_url, :github_account_login, :github_account_type | ||
| ]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having the adapter implicitly doing validation of the repo payload, I switched to simply having the process (and the transaction) erroring out if there are any validation errors on any of the repos.
| {:error, :repo, :unmatched_project, _steps} -> {:ok, []} | ||
| {:error, _errored_step, error_response, _steps} -> {:error, error_response} | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding support for other subtypes of the Issues event, should be relatively simple from here. Most likely, it will be a matter of extending the behavior of the ChangesetBuilder module below.
|
|
||
| case Repo.transaction(multi) do | ||
| {:ok, %{tasks: tasks}} -> {:ok, tasks} | ||
| {:error, :repo, :unmatched_project, _steps} -> {:ok, []} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want the transaction to fail if there are no tasks to create/update due to unmatched projects. If it does not fail, we will end up creating a user for no reason.
However, this transaction failure should not be interpreted as a failure to handle the webhook, so we translate it into a success with no data updated.
| |> Changeset.put_change(:user_id, user_id) | ||
| |> Changeset.validate_required([:project_id, :user_id, :markdown, :body, :title]) | ||
| |> Changeset.assoc_constraint(:project) | ||
| |> Changeset.assoc_constraint(:user) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely, as we add new event subtypes for Issues, the actual changes will happen here. We may be creating TaskUser records alongside the main record, etc.
| %User{} | ||
| |> Changeset.change(user_attrs |> UserAdapter.from_github_user()) | ||
| |> Repo.insert | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to introduce the concept of something like an IdentityProvider record linked to a user record, which is something we've mentioned in the past, that would likely affect the UserLinker module here.
| "id" => _, "title" => _, "body" => _, "state" => _, | ||
| "user" => %{"id" => _} | ||
| }, | ||
| "repository" => %{"id" => _}}), do: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function returns true if the payload possesses the minimum required data to handle the Issues webhook.
Basically this is our "first line of defense", to make sure nothing has changed with the Github API spec and the structure of the payload is as we expect it to be.
| def up do | ||
| alter table(:users) do | ||
| modify :email, :string, null: true | ||
| modify :username, :string, null: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could technically leave this one out and set the username to be the same as github_username, but I feel it just confuses things as we later merge a new/newly-connected user with this one.
5ed2a9a to
48f27fd
Compare
|
@joshsmith This should be read for a review and, hopefully, merge Final process:
When handling
Mapping of issue -> task
Mapping of github user --> user
Not other mapping is performed. User is created without |
joshsmith
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to submit this review until way late in the evening. Everything looks generally fine. Some small comments/questions here, but nothing preventing merge if addressed.
We do need to loop back again in #849, and I'm sure I just need to see this in action to understand all the possible edge cases here, but it looks like much of the main path is figured out here.
| @@ -0,0 +1,86 @@ | |||
| defmodule CodeCorps.Adapter.MapTransformer do | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tested this still works with Stripe locally?
| {:github_account_id, ["owner", "id"]}, | ||
| {:github_account_avatar_url, ["owner", "avatar_url"]}, | ||
| {:github_account_login, ["owner", "login"]}, | ||
| {:github_account_type, ["owner", "type"]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for consistency's sake, would clean up alpha ordering here (and wherever possible).
| {:error, :unmatched_repository} | ||
|
|
||
| @unimplemented_actions ~w(assigned unassigned milestoned demilestoned labeled unlabeled) | ||
| @implemented_actions ~w(opened closed edited reopened) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might change ordering of these.
48f27fd to
26b10ff
Compare
26b10ff to
5f48b41
Compare
Closes #828
This PR started out as a handler for the Issues "opened" webhook exclusively, but I managed to find a generalized solution for all 4 basic subtypes of this webhook.
I also believe we will be able to extend this generalised solution for other subtypes in the future.
This is still not done, but there is an uncertainty we need to discuss, so I'm creating a PR for that purpose.
What the handler does
GithubRepofor the issue{:error, :unmatched_repo}is returned, meaning the event itself will be marked as erroredProjectGithubRepochild of theGithubRepo(found bygithub_id,project_id), it willTaskUser(also found by github id)What I'm unsure of is the following:
in cases other than the "opened" event, should we be creating aYes, we should be creating a taskTaskin case one was not found. Presumably, as theProjectwas connected to aGithubRepo, a sync was performed, so all tasks should be present, meaning this should not actually be occurring, but to code allows it, so I'm not enthusiastic about just ignoring it.if we do not find aWe should be creating a userUserwhat should we do? This is a realistic option and something we've discussed in the past, but we never agreed on a solution fully. Do we hold a CodeCorps user locally, to assign to the Task? Do we store theuser_github_idas a Task field, to be joined later? Do we create a user, which can later be merged with a newly registered/connected user?TODO
StateManagerValidatorHandlerChangesetBuilder