User invites
What's in this PR?
This PR adds API support for UserInvite creation and claims.
The Issue this is referring to specifies that we should be adding an InvitedUser model, but UserInvite seems more consistent in naming, so I opted for that.
Creation of user invites
- we have a new controller for this, as well as a new endpoint
- we differentiate between a plain invite and an invite created to invite a user to a project
- when creating a plain invite, the user is authorized to do so if they are the inviter, with no other conditions
- when creating a project invite, the user needs to be an inviter, and additionally, they need to be of a high enough membership in the specified project, in order to invite a user with a specified role
- pending users and contributors (?) can't do anything
- admins can invite other pending (not useful) and contributor users
- owners can do the same as admins and also invite other admin users
- during creation of the invite, we require an email and also validate that a user account with that email doesn't exist already
Claiming of user invites
This is done through the user controller create action. The user creation was extracted into Accounts.create/1 and a new, special clause matches against a payload holding an "invite_id" key, to differentiate from normal account creation.
When claiming the invite we
- try to load the invite by specified id - can fail and renders a 404 if it does
- create the user (this can fail with a validation error if the user has been created regularly since the invite was created)
- create a project user if a project and role are specified by the invite
- associate invite with created user through
invitee
The user controller is where we track any claimed invites.
Suggested issues to create
- [ ] Add an extra step to normal account creation, which claims all invites associated with the created user's email
- [ ] Add the same extra step to invite claim account creation, which claims all invites other than the one currently being claimed.
- [ ] Add support for inviting existing users to project
References
Closes #1350
Several questions then
- will a project admin/owner ever be able to invite an existing codecorps user to their project?
- would that not create a
UserInviteand create something else? Maybe aProjectUserrecord with a status of"invited"?
Yes, they would be able to invite an existing user. But this would be simply a ProjectUser with a different status, as you suggested. There wouldn't be a need for a UserInvite in this case. I'm not sure of a better name to represent the idea, but the invite is to someone who is not yet a user.
- what happens in the following scenario:
- invite is created for a "[email protected]"
- later, independently, a user registers with that email
- even later, that same user tries to claim the invite
Hadn't thought of this. Ideally upon registration we would look through the UserInvite table and create any ProjectUser records, thereby "claiming" the invite. And again ideally, we would then let a user know that an invite is claimed when they go to a URL with the invite id in the query params, and we would also have a status for such a claim that differentiates it from a sign up with the invite context.
@joshsmith I think this is finally in a mergeable state. There are issues to be created to expand this further, pending feedback, but other than that, I think our initial bases are covered. I'll update the PR description with the current state of the PR
@begedin remember to explicitly re-request a review from me so that it will show up in my reviews.
Additional tracking to implement here:
-
When an invite gets created
- [x]
current_user.id |> track("Created User Invite", user_invite) - [ ]
user_invite.email |> track("User Invited", user_invite) - if invite is for project as well
- [ ]
current_user.id |> track("Invited to Project", user_invite) - [ ]
user_invite.email |> track("Invited to Project (Invitee)", user_invite)
- [ ]
- [x]
-
When an invite gets claimed
- [x]
current_user.id |> track("Claimed User Invite", user_invite) - [ ]
user_invite.email |> track("User Invited", user_invite) - if invite is for project as well
- [ ]
current_user.id |> track("Invited to Project", user_invite) - [ ]
user_invite.email |> track("Invited to Project (Invitee)", user_invite)
- [ ]
- [x]
I actually think the project specific ones could be ignored. For those, we would could have funnels with custom events instead, since the distinct_id is the same as the regular event and the difference is in the project_id property being present or not.
I'm not sure if we might also need a
- [ ]
user_invite.project_id |> track("Invited to Project", user_invite)
@joshsmith I got a question on tracking based on info from #1364
The rest of the code is good for another review.