Description
Extracted from #815
Now that we've been requiring verified email addresses for publishing, it's more likely this feature will work well.
This is a big task that's more complicated than it looks! Please ask questions if anything is confusing! This task would be great for someone looking to get experience with just about every part of crates.io: the frontend, the backend API, the database, email sending.
What should happen:
- When any owner of a crate (either by being an individual owner or having permission through a team) publishes a new version of a crate they own,
- Email all individual owners of that crate that have verified email addresses to notify them of the new version
- We could email all team owners as well, but we'd have to query GitHub for that each time and try to match up team members to crates.io accounts, seems complex. Let's start with individual owners.
- We could email the person who published the crate as well if they have permissions because of a team, so that they're notified more quickly if they didn't authorize that publish with their credentials. However, there'd be no way to turn this email off because we don't have a record on which to store that preference? I'm torn about whether we say "the person who did the publish always gets an email even if you've turned notifications off" or whether we say "people who have permissions via teams never get emails even if they're the one who published the crate". Open to arguments.
- The email should include the name of the crate, the version published, who published it, the date and time they published it, instructions on what to do if the publish wasn't authorized, and a link to the page to opt out of the emails. Here's an example of an email from rubygems.org:
- Let each owner of a crate opt out of getting these notifications for each crate they're an individual owner of. Rubygems.org has this preference page:
Implementation instructions
Roughly trying to separate these into pieces that can be submitted and reviewed as separate PRs; let me know if you run into any problems with the way I've split the tasks up.
-
Backend: email preference storing (addressed in backend: Add user email preference storing #1901)
- Create a migration (search for
diesel migration generate
in this guide) that adds a column to thecrate_owners
table... this isn't great because teams are also stored in this table and teams don't have emails, but I think this is the best spot for now. Name the column something likeemail_notifications
, of type boolean, and set the default to true. In the database dump configuration, set this new column toprivate
(we shouldn't include this value in public database dumps). - Create a function that updates the email notifications field with a given value; it'll look something like the
owner_remove
function. - Create a new API route in the router that's a PUT request to
/me/email_notifications
or similar and calls a new controller function that I think would fit in theuser/me
controller, calledupdate_email_notifications
or similar - That controller should get a list of crate IDs and on/off values from radio buttons in the frontend about whether the current user wants to get email notifications for that crate or not. Iterate through the crate IDs, fetch any crate owner records for the current user and the given IDs (ignore IDs for crates that the current user doesn't actually own), and update the
email_notifications
column of those records with the values sent. Updating the values unconditionally is fine, I think. Ignore any crates that the current user owns that we didn't get any radio button value for. - So whether you have email notifications for a crate you own on or off should be private information. That makes me inclined to include this information in with the GET /me request and the
encodable_private
response that uses theEncodablePrivate
struct. Minimally, this should be a vector of (crate name, crate id, email notification value) tuples, I think. Enough to display the form with the radio buttons described below. - Write tests for the new update controller action and the new info in the /me response in with the other user tests.
- Create a migration (search for
-
Frontend: settings (addressed in front-end: Add email notification preferences #1913)
- Given that I think we should include the email setting values in with the
/me
API request, I think the frontend should add a new section to https://crates.io/me whose HTML is here, as opposed to creating a whole separate page for email notifications. - This section should list all crates the current user owns as listed in the API response. My ember knowledge is still lacking, so I'm not sure how to describe how to do this the right way.
- There should be a form with radio buttons for each crate id and whether emails are on/off, and a button to save all the settings that sends the radio button values to the backend
/me/email_notifications
controller described above. - If there's a good way to test this in the ember tests, please add some. I don't think there's going to be too much frontend logic though?
- Given that I think we should include the email setting values in with the
-
Backend: email sending (no PR yet)
- After the crate has been successfully added to the index, call a new
notify_owners
function. So use?
onrepo.commit_and_push
, and instead return the result fromnotify_owners
in thisadd_crate
function. - In
add_crate
, theCrate
parameter is of this type, which currently doesn't have the owners available on it, nor should it because that is what gets serialized into the index. Theadd_crate
function doesn't currently use a database connection. Therefore, I'm thinkingadd_crate
should have a new parameter that's a vector of email addresses. Thenadd_crate
can callnotify_owners
with the email addresses, thekrate.name
,krate.vers
, andchrono::Utc::now()
. - So in the publish controller, before
git::add_crate
is called, we'll need to get the unique set of verified email addresses of the named owners plus the verified email address of the person who published the crate, if we decide we want to do that (see bolded bullet point at the top where I'm conflicted about this). We already have the set of crate owners available, which is aVec
ofOwner
s. So iterate through the owners, and for user owners, if theiremail_notifications
value istrue
(the new column described above), collect the present verified email addresses into aHashSet
(to get unique ones) and (maybe, see above) add the publisher's verified email address. - The new
notify_owners
function will probably look liketry_send_user_confirm_email
and can go in that file. Use the screenshot above of the rubygems email as a general guide for what should be in the email, and ask questions here if you have any. - We don't have a great way to write automated tests for emails sent from background jobs, but do write tests for gathering the email addresses of the owners.
- After the crate has been successfully added to the index, call a new