Experiment: "Build iOS app guide" email campaign
-
Review changes -
-
Download -
Patches
-
Plain diff
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:
- Remove
NOT NULL
constraints ontrack
andseries
columns - Add
campaign
(text
) column (for now it can only contain'build_ios_app_guide'
- Add unique multicolumn index on
user_id
andcampaign
to ensure that a user is sent a campaign email at most once - Add a check constraint to ensure that
track
andseries
areNOT NULL
whencampaign IS NULL
ORtrack
andseries
areNULL
whencampaign 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
How to set up and validate locally
To test just the email itself, expand this to see instructions:
- Start up your local GDK instance
- In Rails console
Notify.build_ios_app_guide_email('test_recipient@example.com').deliver_now
- Visit http://localhost:3000/rails/letter_opener to see the sent email
To test the whole flow:
- Ensure GDK instance is set up to simulate
gitlab.com
. In your terminal, run:export GITLAB_SIMULATE_SAAS=1 gdk start
- Enable the experiment. In Rails console:
Feature.enable(:build_ios_app_guide_email)
- Create a project using iOS (Swift) template
- 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
- 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:
- Push a commit to the main branch (
master
) of the project. You can do this by just editing theREADME.md
file using Web IDE and committing the change tomaster
branch - 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.
-
I have evaluated the MR acceptance checklist for this MR.
Merge request reports
- version 379c9b3010
- version 36f8e39220
- version 35f8e39220
- version 34e4f0cf47
- version 33e4f0cf47
- version 32e4f0cf47
- version 31ae9cbedc
- version 300d258485
- version 29967a6997
- version 2856b60838
- version 2756b60838
- version 2668190cbf
- version 2568190cbf
- version 24c8809ce7
- version 23872829f7
- version 229600c591
- version 2185a8ce44
- version 20fb3d6063
- version 19fb3d6063
- version 184145c67f
- version 173177542d
- version 169f398dde
- version 15c26f14fc
- version 14fe9abc83
- version 13c79fd3e6
- version 1203f048f5
- version 11f21886f6
- version 1043051e52
- version 9e27d9ff1
- version 8b7b4da24
- version 7ebbf03e2
- version 6fb7006df
- version 510f215fa
- version 43d74f2b7
- version 324362c00
- version 29fa545ad
- version 1fefd2bea
- master (base)
- latest versionde7672c013 commits,
- version 379c9b301012 commits,
- version 36f8e3922011 commits,
- version 35f8e3922011 commits,
- version 34e4f0cf4713 commits,
- version 33e4f0cf4713 commits,
- version 32e4f0cf47907 commits,
- version 31ae9cbedc11 commits,
- version 300d25848512 commits,
- version 29967a69977 commits,
- version 2856b608387 commits,
- version 2756b608387 commits,
- version 2668190cbf8 commits,
- version 2568190cbf7 commits,
- version 24c8809ce77 commits,
- version 23872829f75 commits,
- version 229600c5914 commits,
- version 2185a8ce444 commits,
- version 20fb3d606317 commits,
- version 19fb3d606317 commits,
- version 184145c67f16 commits,
- version 173177542d16 commits,
- version 169f398dde15 commits,
- version 15c26f14fc14 commits,
- version 14fe9abc836 commits,
- version 13c79fd3e63 commits,
- version 1203f048f57 commits,
- version 11f21886f67 commits,
- version 1043051e526 commits,
- version 9e27d9ff13 commits,
- version 8b7b4da242 commits,
- version 7ebbf03e24 commits,
- version 6fb7006df4 commits,
- version 510f215fa4 commits,
- version 43d74f2b73 commits,
- version 324362c001 commit,
- version 29fa545ad1 commit,
- version 1fefd2bea1 commit,
- Side-by-side
- Inline