Skip to content
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

Mailchimp unsubscribe webhook #5804

Merged
merged 5 commits into from
Feb 11, 2020

Conversation

citizen428
Copy link
Contributor

@citizen428 citizen428 commented Jan 29, 2020

What type of PR is this? (check all applicable)

  • Bug Fix

Description

When user's directly unsubscribe from Mailchimp, we don't have any record of that and keep sending emails. This PR adds a webhook endpoint so we get notified of unsubscribe events (docs.

Related Tickets & Documents

https://github.com/thepracticaldev/tech-private/issues/289

Mobile & Desktop Screenshots/Recordings (if there are UI changes)

Added to documentation?

  • docs.dev.to
  • readme
  • no documentation needed

@citizen428 citizen428 requested a review from a team January 29, 2020 07:22
@rhymes rhymes removed the request for review from a team January 29, 2020 13:43
class Webhooks::MailchimpUnsubscribesController < ApplicationController
class InvalidListID < StandardError; end

LIST_MAPPINGS = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way to do this lookup/is this mapping defined somewhere already?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the first of it's kind 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, good to know. Can you please sanity check the mapping?


def create
not_authorized unless valid_secret?
user = User.find_by!(email: params.dig(:data, :email))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to raise in case we can't find the user, but I'm open to other approaches (log to DataDog and no-op for example).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fine for the time being. It should be caught by honeybadger.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should not raise while still testing this out/debugging, because if my assumption about the webhook payload are wrong we'll raise for no good reason.

@@ -154,11 +154,15 @@
resources :reads, only: [:create]
end

namespace :webhooks do
post "/mailchimp/:secret/unsubscribe", to: "mailchimp_unsubscribes#create", as: :mailchimp_unsubscribe
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably change this at one point and use routing DSL trickery, but for now this seems good enough.

@maestromac
Copy link
Contributor

I think this is a very solid first iteration

@citizen428 citizen428 changed the title [WIP] Mailchimp unsubscribe webhook Mailchimp unsubscribe webhook Feb 5, 2020
@pr-triage pr-triage bot added the PR: unreviewed bot applied label for PR's with no review label Feb 5, 2020
@citizen428
Copy link
Contributor Author

I think this is a very solid first iteration

In light of this I removed the WIP tag now. Just let me know what we should regarding the exception if we can't find the user (I left a comment), then we could take this for a spin.

Copy link
Contributor

@rhymes rhymes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not 100% sure we should these things webhooks as it maybe confusing with user registered webhooks for outgoing events but I also don't have a better name 🗡

Good job @citizen428

config/routes.rb Outdated Show resolved Hide resolved
@@ -0,0 +1,30 @@
class Webhooks::MailchimpUnsubscribesController < ApplicationController
class InvalidListID < StandardError; end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should all this logic (the mappings and the email_type) be in the controller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a POC, I opted for "easy to remove again" as my optimization target. If this sticks around, we can always move it out later. But if you prefer I can also move it out now, but since this mapping isn't used elsewhere yet, I don't have a problem with it being in this controller for the time being.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it makes sense, let's leave it as is

@citizen428
Copy link
Contributor Author

I'm still not 100% sure we should these things webhooks as it maybe confusing with user registered webhooks

How about IncomingWebhooks or WebhookCallbacks?

@rhymes
Copy link
Contributor

rhymes commented Feb 6, 2020

How about IncomingWebhooks

Great idea!

@citizen428 citizen428 changed the title Mailchimp unsubscribe webhook [WIP] Mailchimp unsubscribe webhook Feb 6, 2020
@pr-triage pr-triage bot removed the PR: unreviewed bot applied label for PR's with no review label Feb 6, 2020
@citizen428
Copy link
Contributor Author

Changed back to WIP as I'll do some renaming tomorrow.

@citizen428 citizen428 changed the title [WIP] Mailchimp unsubscribe webhook Mailchimp unsubscribe webhook Feb 7, 2020
@pr-triage pr-triage bot added the PR: unreviewed bot applied label for PR's with no review label Feb 7, 2020
@citizen428
Copy link
Contributor Author

@rhymes @maestromac I renamed this to IncomingWebhooks, much less potential for confusion. Please review again.

Copy link
Contributor

@rhymes rhymes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @citizen428!

What are the next steps when this is merged?

RSpec.describe "IncomingWebhooks::MailchimpUnsubscribesController", type: :request do
let(:user) { create(:user, email_digest_periodic: true) }

describe "POST /webhooks/mailchimp/:secret/unsubscribe" do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's a typo here but I don't think it should hold the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that it should say incoming_webhooks? Because I don't really see a typo, but that could also be because I knew what I wanted to write 😉

@pr-triage pr-triage bot added PR: reviewed-approved bot applied label for PR's where reviewer approves changes and removed PR: unreviewed bot applied label for PR's with no review labels Feb 10, 2020
Comment on lines +6 to +8
mailchimp_sustaining_members_id: :email_membership_newsletter,
mailchimp_tag_moderators_id: :email_tag_mod_newsletter,
mailchimp_community_moderators_id: :email_community_mod_newsletter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from what i can tell, we're not using most of these keys; what do we need them for? (just trying to get context on this)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the mapping of option names in the SiteConfig model to newsletter types. We're using it here:

    list_id = params.dig(:data, :list_id)
    key = LIST_MAPPINGS.keys.detect { |k| SiteConfig.public_send(k) == list_id }

Once we have mapped from list id to attribute name, we use the latter in the update user.update(email_type => false).

Does that clear it up?


expect do
post "/incoming_webhooks/mailchimp/#{secret}/unsubscribe", params: params
end.to change { user.reload.email_newsletter }.from(true).to(false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, i didn't know about the from(true).to(false) syntax! that's so much nicer than asserting the value of email_newsletter on the user before making the post request, and then checking it again afterwards!! ✨

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also by, which can specify an amount of change, i.e.

expect do
  User.create(...)
end.to change(User, :count).by(1)

@citizen428
Copy link
Contributor Author

@rhymes The next step would be to use @maestromac's account to subscribe/unsubscribe and confirm some of my assumptions around the payload shape etc.

@maestromac maestromac merged commit 050a2e2 into master Feb 11, 2020
@pr-triage pr-triage bot added PR: merged bot applied label for PR's that are merged and removed PR: reviewed-approved bot applied label for PR's where reviewer approves changes labels Feb 11, 2020
@maestromac maestromac deleted the citizen428/mailchimp-webhook-tech-private-289 branch February 11, 2020 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: merged bot applied label for PR's that are merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants