Skip to content
Snippets Groups Projects

Experiment: "Build iOS app guide" email campaign

Merged Eugie Limpin requested to merge experiment/publish-ios-apps-with-fastlane into master

What does this MR do and why?

This MR implements a campaign email experiment.

In !80361 (merged) we introduced a service running in the background to detect if a project is an iOS project. This detection happens every time changes are pushed to the project's main branch (much like repository language detection).

In this MR, when a project is determined to be an iOS project we send an email to the project's members with access_level >= DEVELOPER that contains a guide on how to build iOS projects within GitLab.

Database changes

Users::InProductMarketingEmail is updated so it can be re-used to record sending of campaign emails. Previously, this model was only used to record sending of marketing emails (Users::InProductMarketingEmail records with track and series attribute values) when users accomplish specific tasks during onboarding–e.g. user invites a teammate to a project.

This update comprises the following changes to in_product_marketing_emails DB table:

  1. Remove NOT NULL constraints on track and series columns
  2. Add campaign (text) column (for now it can only contain 'build_ios_app_guide'
  3. Add unique multicolumn index on user_id and campaign to ensure that a user is sent a campaign email at most once
  4. Add a check constraint to ensure that track and series are NOT NULL when campaign IS NULL OR track and series are NULL when campaign IS NOT NULL

Migrations

Rollback

AddTextLimitToInProductMarketingEmailCampaign
$ rails db:rollback
== 20220420034519 AddTextLimitToInProductMarketingEmailCampaign: reverting ====
-- transaction_open?()
   -> 0.0000s
-- transaction_open?()
   -> 0.0000s
-- execute("ALTER TABLE in_product_marketing_emails\nDROP CONSTRAINT IF EXISTS check_9d8b29f74f\n")
   -> 0.0062s
== 20220420034519 AddTextLimitToInProductMarketingEmailCampaign: reverted (0.0217s)
AddCampaignToInProductMarketingEmail
$ rails db:rollback
== 20220401071609 AddCampaignToInProductMarketingEmail: reverting =============
-- transaction_open?()
   -> 0.0000s
-- transaction_open?()
   -> 0.0000s
-- execute("ALTER TABLE in_product_marketing_emails\nDROP CONSTRAINT IF EXISTS in_product_marketing_emails_track_and_series_or_campaign\n")
   -> 0.0012s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:in_product_marketing_emails, [:user_id, :campaign], {:name=>:index_in_product_marketing_emails_on_user_campaign, :algorithm=>:concurrently})
   -> 0.0039s
-- execute("SET statement_timeout TO 0")
   -> 0.0005s
-- remove_index(:in_product_marketing_emails, {:name=>:index_in_product_marketing_emails_on_user_campaign, :algorithm=>:concurrently, :column=>[:user_id, :campaign]})
   -> 0.0046s
-- execute("RESET statement_timeout")
   -> 0.0005s
-- remove_column(:in_product_marketing_emails, :campaign, {:if_exists=>true})
   -> 0.0022s
== 20220401071609 AddCampaignToInProductMarketingEmail: reverted (0.0305s) ====

Migrate

AddCampaignToInProductMarketingEmail
$ rails db:migrate
== 20220401071609 AddCampaignToInProductMarketingEmail: migrating =============
-- change_column_null(:in_product_marketing_emails, :track, true)
   -> 0.0014s
-- change_column_null(:in_product_marketing_emails, :series, true)
   -> 0.0008s
-- add_column(:in_product_marketing_emails, :campaign, :text)
   -> 0.0037s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:in_product_marketing_emails, [:user_id, :campaign], {:unique=>true, :name=>:index_in_product_marketing_emails_on_user_campaign, :algorithm=>:concurrently})
   -> 0.0041s
-- execute("SET statement_timeout TO 0")
   -> 0.0005s
-- add_index(:in_product_marketing_emails, [:user_id, :campaign], {:unique=>true, :name=>:index_in_product_marketing_emails_on_user_campaign, :algorithm=>:concurrently})
   -> 0.0067s
-- execute("RESET statement_timeout")
   -> 0.0005s
-- transaction_open?()
   -> 0.0000s
-- current_schema()
   -> 0.0003s
-- transaction_open?()
   -> 0.0000s
-- execute("ALTER TABLE in_product_marketing_emails\nADD CONSTRAINT in_product_marketing_emails_track_and_series_or_campaign\nCHECK ( (track IS NOT NULL AND series IS NOT NULL AND campaign IS NULL) OR (track IS NULL AND series IS NULL AND campaign IS NOT NULL) )\nNOT VALID;\n")
   -> 0.0018s
-- current_schema()
   -> 0.0002s
-- execute("ALTER TABLE in_product_marketing_emails VALIDATE CONSTRAINT in_product_marketing_emails_track_and_series_or_campaign;")
   -> 0.0010s
== 20220401071609 AddCampaignToInProductMarketingEmail: migrated (0.0444s) ====
AddTextLimitToInProductMarketingEmailCampaign
$ rails db:migrate
== 20220420034519 AddTextLimitToInProductMarketingEmailCampaign: migrating ====
-- transaction_open?()
   -> 0.0000s
-- current_schema()
   -> 0.0019s
-- transaction_open?()
   -> 0.0000s
-- execute("ALTER TABLE in_product_marketing_emails\nADD CONSTRAINT check_9d8b29f74f\nCHECK ( char_length(campaign) <= 255 )\nNOT VALID;\n")
   -> 0.0011s
-- current_schema()
   -> 0.0003s
-- execute("ALTER TABLE in_product_marketing_emails VALIDATE CONSTRAINT check_9d8b29f74f;")
   -> 0.0008s
== 20220420034519 AddTextLimitToInProductMarketingEmailCampaign: migrated (0.0131s)

Screenshots or screen recordings

Screen_Shot_2022-04-12_at_5.22.55_PM

How to set up and validate locally

To test just the email itself, expand this to see instructions:
  1. Start up your local GDK instance
  2. In Rails console
    Notify.build_ios_app_guide_email('test_recipient@example.com').deliver_now
  3. Visit http://localhost:3000/rails/letter_opener to see the sent email

To test the whole flow:

  1. Ensure GDK instance is set up to simulate gitlab.com. In your terminal, run:
    export GITLAB_SIMULATE_SAAS=1 gdk start
  2. Enable the experiment. In Rails console:
    Feature.enable(:build_ios_app_guide_email)
  3. Create a project using iOS (Swift) template
  4. Add a developer and a guest (members) to the project
    • Ensure that all users (owner, developer and guest) during testing are opted in to receive marketing emails and can receive notifications. You can check this in Rails console:
      user = User.find_by_username('username_of_user')
      user.email_opted_in? # should be true. If it's not then just do user.update({email_opted_in: true})
      user.can?(:receive_notifications) # should be true
  5. Push a commit to the main branch (master) of the project. You can do this by just editing the README.md file using Web IDE and committing the change to master branch
  6. Visit http://localhost:3000/rails/letter_opener and verify that:
    • the email is sent to the owner and the invited developer member of the project
    • the email is NOT sent to the invited guest member of the project

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Eugie Limpin

Merge request reports

Loading
Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
  • Alex Buijs
  • Alex Buijs
  • Alex Buijs
  • Alex Buijs
  • Alex Buijs
  • Eugie Limpin added 635 commits

    added 635 commits

    Compare with previous version

  • Eugie Limpin added 2 commits

    added 2 commits

    • 82de69d2 - Extend InProductMarketingEmail to be used for other email campaigns
    • b7b4da24 - Create email template

    Compare with previous version

  • Eugie Limpin added 2 commits

    added 2 commits

    • 609a37ee - Send campaign email after platform detection
    • e27d9ff1 - Move logic to its own service

    Compare with previous version

  • Eugie Limpin added 3 commits

    added 3 commits

    • 5079bc8d - Implement email sending logic as an experiment
    • 5b01b1f3 - Add tests for Users::InProductMarketingEmailCampaignService
    • 43051e52 - WIP: Tests outline

    Compare with previous version

  • Eugie Limpin added 1 commit

    added 1 commit

    • f21886f6 - Users::InProductMarketingEmail model specs

    Compare with previous version

  • Eugie Limpin added 6 commits

    added 6 commits

    • 6b815ded - Send campaign email after platform detection
    • c6059ffe - Move logic to its own service
    • 73270cb9 - Implement email sending logic as an experiment
    • 134f2c33 - Add tests for Users::InProductMarketingEmailCampaignService
    • a74cde0d - Add tests for RecordTargetPlatformsService
    • 03f048f5 - Users::InProductMarketingEmail model specs

    Compare with previous version

  • Eugie Limpin added 3 commits

    added 3 commits

    • a750efef - Extend InProductMarketingEmail to be used for other email campaigns
    • 5ec94254 - Send campaign email after platform detection
    • c79fd3e6 - Move common methods to helper

    Compare with previous version

  • Eugie Limpin added 3 commits

    added 3 commits

    • e77c9d70 - Add without_campaign scope
    • e310df22 - Update InProductMarketingEmailRecords to accept campaign
    • fe9abc83 - Update services to send emails to all project members

    Compare with previous version

  • Eugie Limpin mentioned in merge request !84696 (merged)

    mentioned in merge request !84696 (merged)

  • Eugie Limpin added 8 commits

    added 8 commits

    • a6c87ead - Implement send email to developers or higher access levels
    • bc378f47 - Fix rubocop errors
    • 43a762bb - Fix rubocop errors in migration file
    • 857e703a - Remove unused scope
    • d698608e - Add specs for without_campaign scope
    • 7461a085 - Move not under EE
    • 4a065232 - Move under Users namespace
    • c26f14fc - Use symbol as index name and add commentary

    Compare with previous version

  • Eugie Limpin changed the description

    changed the description

  • Eugie Limpin added 2 commits

    added 2 commits

    • a71f3a2b - Update migration rollback method to delete campaign email records
    • 9f398dde - Remove logic to only send email when target_platforms has been updated

    Compare with previous version

  • Eugie Limpin added 1 commit

    added 1 commit

    Compare with previous version

  • Eugie Limpin added 16 commits

    added 16 commits

    • 40cf3175 - Move methods to build email unsubscribe link to helper
    • 886815c0 - Extend InProductMarketingEmail to be used for other email campaigns
    • f25bbda2 - Send campaign email after platform detection
    • 1007187d - Add without_campaign scope
    • b2c0e482 - Update InProductMarketingEmailRecords to accept campaign
    • 23a87628 - Update services to send emails to all project members
    • bef22d69 - Implement send email to developers or higher access levels
    • e823fc76 - Fix rubocop errors
    • daf8c58b - Fix rubocop errors in migration file
    • dfe8374a - Remove unused scope
    • 978b84d6 - Add specs for without_campaign scope
    • 5e769a3b - Move not under EE
    • 1c9e52cc - Move under Users namespace
    • 60da0942 - Update migration rollback method to delete campaign email records
    • 620c9a89 - Remove logic to only send email when target_platforms has been updated
    • 4145c67f - Fix rubocop errors

    Compare with previous version

  • Eugie Limpin added 1 commit

    added 1 commit

    • fb3d6063 - Use unsubscribe method from helper

    Compare with previous version

  • Eugie Limpin changed the description

    changed the description

  • Eugie Limpin changed target branch from master to dry-refactor-email-unsubscribe-link-methods

    changed target branch from master to dry-refactor-email-unsubscribe-link-methods

  • Eugie Limpin added 5 commits

    added 5 commits

    • 10931f25 - 1 commit from branch dry-refactor-email-unsubscribe-link-methods
    • dd21859a - Update InProductMarketingEmail to track campaign emails
    • 042b6f9d - Update InProductMarketingEmailRecords to handle campaigns
    • ed114b90 - Send campaign email after platform detection
    • 85a8ce44 - Move InProductMarketingEmailRecords under Users namespace

    Compare with previous version

  • Eugie Limpin
  • Eugie Limpin marked the checklist item I have evaluated the MR acceptance checklist for this MR. as completed

    marked the checklist item I have evaluated the MR acceptance checklist for this MR. as completed

  • Eugie Limpin changed the description

    changed the description

  • Eugie Limpin changed the description

    changed the description

  • Eugie Limpin changed the description

    changed the description

  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Please register or sign in to reply
    Loading