Experiment: "Build iOS app guide" email campaign
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
Activity
changed milestone to %14.10
added typefeature label
assigned to @eugielimpin
2 Warnings This merge request is quite big (655 lines changed), please consider splitting it into multiple merge requests. This merge request includes more than 10 commits. Each commit should meet the following criteria: - Have a well-written commit message.
- Has all tests passing when used on its own (e.g. when using git checkout SHA).
- Can be reverted on its own without also requiring the revert of commit that came before it.
- Is small enough that it can be reviewed in isolation in under 30 minutes or so.
If this merge request contains commits that do not meet this criteria and/or contains intermediate work, please rebase these commits into a smaller number of commits or split this merge request into multiple smaller merge requests.
If needed, you can retry the
danger-review
job that generated this comment.Reviewer roulette
Changes that require review have been detected!
Please refer to the table below for assigning reviewers and maintainers suggested by Danger in the specified category:
Category Reviewer Maintainer backend Eugenia Grieff ( @egrieff
) (UTC+1, 7 hours behind@eugielimpin
)Doug Stull ( @dstull
) (UTC-4, 12 hours behind@eugielimpin
)database Matt Kasa ( @mattkasa
) (UTC+0, 8 hours behind@eugielimpin
)Steve Abrams ( @sabrams
) (UTC-6, 14 hours behind@eugielimpin
)frontend Miranda Fluharty ( @mfluharty
) (UTC+1, 7 hours behind@eugielimpin
)Natalia Tepluhina ( @ntepluhina
) (UTC+2, 6 hours behind@eugielimpin
)~migration No reviewer available No maintainer available To spread load more evenly across eligible reviewers, Danger has picked a candidate for each review slot, based on their timezone. Feel free to override these selections if you think someone else would be better-suited or use the GitLab Review Workload Dashboard to find other available reviewers.
To read more on how to use the reviewer roulette, please take a look at the Engineering workflow and code review guidelines. Please consider assigning a reviewer or maintainer who is a domain expert in the area of the merge request.
Once you've decided who will review this merge request, assign them as a reviewer! Danger does not automatically notify them for you.
Generated by
DangerSetting label groupactivation based on
@eugielimpin
's group.added groupactivation label
Setting label(s) devopsgrowth sectiongrowth based on groupactivation.
added devopsgrowth sectiongrowth labels
added 324 commits
-
fefd2bea...134eecdc - 323 commits from branch
master
- 9fa545ad - Create email template
-
fefd2bea...134eecdc - 323 commits from branch
Allure report
allure-report-publisher
generated test report!review-qa-smoke:
test report
review-qa-reliable: test report
review-qa-blocking: test reportreview-qa-blocking:
test report for de7672c0+-------------------------------------------------------------------+ | suites summary | +----------------------+--------+--------+---------+-------+--------+ | | passed | failed | skipped | flaky | result | +----------------------+--------+--------+---------+-------+--------+ | Verify | 12 | 0 | 1 | 4 | ❗ | | Protect | 3 | 0 | 0 | 0 | ✅ | | Create | 16 | 0 | 2 | 2 | ❗ | | Plan | 41 | 0 | 1 | 2 | ❗ | | Package Registry | 6 | 0 | 0 | 6 | ❗ | | Manage | 28 | 0 | 2 | 16 | ❗ | | SSH keys support | 2 | 0 | 0 | 0 | ✅ | | Package | 0 | 0 | 1 | 0 | ➖ | | Version sanity check | 0 | 0 | 1 | 0 | ➖ | | Configure | 0 | 0 | 1 | 0 | ➖ | +----------------------+--------+--------+---------+-------+--------+ | Total | 108 | 0 | 9 | 30 | ❗ | +----------------------+--------+--------+---------+-------+--------+
added 1 commit
- 10f215fa - Add campaign column to Users::InProductMarketingEmail
added 1 commit
- fb7006df - Extend InProductMarketingEmail to be used for other email campaigns
- Resolved by Alex Buijs
- Resolved by Alex Buijs
- Resolved by Alex Buijs
- Resolved by Alex Buijs
- Resolved by Alex Buijs
- Resolved by Steve Abrams
added 635 commits
-
fb7006df...f4fd015d - 631 commits from branch
master
- ed95968c - Create email template
- 1484b4f0 - update subject
- c996b61d - Refactor
- ebbf03e2 - Extend InProductMarketingEmail to be used for other email campaigns
Toggle commit list-
fb7006df...f4fd015d - 631 commits from branch
added 2 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
Toggle commit listmentioned in merge request !84696 (merged)
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
Toggle commit listadded 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
Toggle commit listadded 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
Toggle commit list-
10931f25 - 1 commit from branch
- Resolved by Natalia Tepluhina
marked the checklist item I have evaluated the MR acceptance checklist for this MR. as completed