Skip to content

Pass UTM parameters to google play#17121

Open
bluewave41 wants to merge 3 commits intomainfrom
WT-1010-pass-utm-params
Open

Pass UTM parameters to google play#17121
bluewave41 wants to merge 3 commits intomainfrom
WT-1010-pass-utm-params

Conversation

@bluewave41
Copy link
Copy Markdown
Collaborator

@bluewave41 bluewave41 commented Apr 7, 2026

If this changeset needs to go into the FXC codebase, please add the WMO and FXC label.

One-line summary

This PR changes auto redirect behavior for Android user agents when redirecting to the app store. UTM parameters are now retained with default values passed if non exist.

Issue / Bugzilla link

https://mozilla-hub.atlassian.net/browse/WT-1010

Testing

@bluewave41 bluewave41 force-pushed the WT-1010-pass-utm-params branch from 2ad4c06 to 03b981a Compare April 8, 2026 15:46
@bluewave41 bluewave41 marked this pull request as ready for review April 8, 2026 15:48
@bluewave41 bluewave41 requested a review from a team as a code owner April 8, 2026 15:48
@stevejalim
Copy link
Copy Markdown
Contributor

Thanks for this @bluewave41 - before I review, would you mind fixing up these failing tests? Let me know if I can help - happy to!

[18](https://github.com/mozilla/bedrock/actions/runs/24144656648/job/70455222374?pr=17121#step:3:8019)
FAILED bedrock/firefox/tests/test_firefox_details.py::TestFirefoxAndroid::test_get_download_url_release
FAILED bedrock/firefox/tests/test_views.py::TestFirefoxThanksAndroidUTMParameters::test_thanks_contains_matching_utm
FAILED bedrock/releasenotes/tests/test_base.py::TestReleaseViews::test_get_download_url_android

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.71%. Comparing base (9b2c6aa) to head (b62ca9e).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #17121   +/-   ##
=======================================
  Coverage   81.70%   81.71%           
=======================================
  Files         174      174           
  Lines        9326     9330    +4     
=======================================
+ Hits         7620     7624    +4     
  Misses       1706     1706           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates Firefox for Android Google Play store links to carry through (or default) UTM campaign parameters via the referrer= query string, so Android “thanks” flows can retain attribution when navigating to the Play Store.

Changes:

  • Remove the static, pre-encoded UTM referrer from the GOOGLE_PLAY_FIREFOX_LINK_UTMS setting and instead construct referrer= dynamically.
  • Thread request UTM values through download_firefoxandroid_buildsFirefoxAndroid.get_download_url(...).
  • Add view-level tests asserting the Android thanks page link contains default/overridden UTM values.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
bedrock/settings/appstores.py Removes the hard-coded encoded referrer from the Play Store URL constant.
bedrock/firefox/firefox_details.py Extends Android download URL generation to append an encoded referrer= built from UTM params.
bedrock/firefox/templatetags/helpers.py Collects UTM params from the request (with defaults) and passes them into Android download URL generation.
bedrock/firefox/tests/test_views.py Adds tests for UTM presence/defaulting/overriding in the Android thanks page Play Store link.

Comment on lines 8 to +12
# Link to Firefox for Android on the Google Play store with Google Analytics
# campaign parameters.
# To clarify below, 'referrer' key value must be a URL encoded string of utm_*
# key/values (https://bugzilla.mozilla.org/show_bug.cgi?id=1099429#c0).
GOOGLE_PLAY_FIREFOX_LINK_UTMS = (
GOOGLE_PLAY_FIREFOX_LINK + "&referrer=" + quote("utm_source=www.mozilla.org&utm_medium=referral&utm_campaign=download")
)
GOOGLE_PLAY_FIREFOX_LINK_UTMS = GOOGLE_PLAY_FIREFOX_LINK
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

fix(blocking): The comments above GOOGLE_PLAY_FIREFOX_LINK_UTMS say this constant includes URL-encoded utm_* referrer params, but it’s now just the bare Play Store URL. This is misleading for future changes; either update the comment (and possibly the constant name) to reflect the new behavior, or restore the encoded default referrer at the settings level if that’s still the intended contract.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, I saw this too and wondered if we can just now use GOOGLE_PLAY_FIREFOX_LINK wherever we have used GOOGLE_PLAY_FIREFOX_LINK_UTMS in the past?

Comment on lines 501 to +509
if channel != "release":
product_id = self.store_product_ids.get(channel, "org.mozilla.firefox")
return self.store_url.replace(self.store_product_ids["release"], product_id)

return self.store_url
return (
self.store_url
+ "&referrer="
+ quote(f"utm_source={utm_params['utm_source']}&utm_medium={utm_params['utm_medium']}&utm_campaign={utm_params['utm_campaign']}")
)
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

fix(blocking): utm_params are ignored for channel != "release" because the function returns early after swapping the product id. Before this change, non-release links inherited the &referrer=... from store_url; now beta/nightly Play Store links will drop referrer/UTM tracking entirely. Consider appending the encoded referrer for non-release channels too (after replacing the product id), or moving referrer building into a shared path so all store links include it consistently.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@willdurand Do you want RTAMO for Android to only work for release or is there a need for supporting beta/nightly too, please?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It wouldn't be a bad idea to support all channels.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looking at the entire patch, it does seem like a regression. I think we should probably not early return when the channel isn't release anymore. Instead, we should update self.store_url, and unconditionally return that + the utm params in the referrer query param as shown in the code snippet above.

Comment on lines 484 to +509
@@ -493,8 +501,12 @@
if channel != "release":
product_id = self.store_product_ids.get(channel, "org.mozilla.firefox")
return self.store_url.replace(self.store_product_ids["release"], product_id)

return self.store_url
return (
self.store_url
+ "&referrer="
+ quote(f"utm_source={utm_params['utm_source']}&utm_medium={utm_params['utm_medium']}&utm_campaign={utm_params['utm_campaign']}")
)
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

fix(blocking): utm_params defaults are only applied when utm_params is None. If a caller passes a dict/QueryDict missing one of the required keys, the utm_params['utm_source'] / ['utm_medium'] / ['utm_campaign'] indexing will raise a KeyError. To make this API robust, merge defaults with any provided mapping (e.g., use .get(...) for each key or overlay provided values onto a default dict) before constructing the referrer.

Copilot uses AI. Check for mistakes.
href = link.attr("href")
assert quote("utm_source=www.mozilla.org") not in href
assert quote("utm_source=www.test.com") in href
assert quote("utm_medium=download") not in href
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

fix(blocking): This assertion checks utm_medium=download is not present, but the default medium being overridden in this PR is utm_medium=referral. As written, the test would still pass even if the default utm_medium=referral incorrectly remained in the URL. Update the assertion to ensure the actual default medium value is absent when overridden.

Suggested change
assert quote("utm_medium=download") not in href
assert quote("utm_medium=referral") not in href

Copilot uses AI. Check for mistakes.
Comment on lines 467 to +509
@@ -474,6 +481,7 @@
instead of Google Play.
:return: string url
"""
utm_params = utm_params or {"utm_source": "www.mozilla.org", "utm_medium": "referral", "utm_campaign": "download"}
if force_direct:
# Use a bouncer link
return "?".join(
@@ -493,8 +501,12 @@
if channel != "release":
product_id = self.store_product_ids.get(channel, "org.mozilla.firefox")
return self.store_url.replace(self.store_product_ids["release"], product_id)

return self.store_url
return (
self.store_url
+ "&referrer="
+ quote(f"utm_source={utm_params['utm_source']}&utm_medium={utm_params['utm_medium']}&utm_campaign={utm_params['utm_campaign']}")
)
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

test(blocking): The new behavior of appending a referrer= parameter (and accepting per-request utm_params) isn’t directly asserted in the existing FirefoxAndroid.get_download_url unit tests (they currently only use startswith(...)). Consider adding focused assertions that (1) release/beta/nightly store URLs include referrer= and the encoded utm_* values, and (2) provided utm_params override defaults as expected, to prevent regressions.

Copilot uses AI. Check for mistakes.
@willdurand
Copy link
Copy Markdown
Member

If this changeset needs to go into the FXC codebase, please add the WMO and FXC label.

I think it does, right?

utm_params = ctx["request"].GET.copy()
utm_params["utm_source"] = utm_params.get("utm_source", "www.mozilla.org")
utm_params["utm_medium"] = utm_params.get("utm_medium", "referral")
utm_params["utm_campaign"] = utm_params.get("utm_campaign", "download")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We'd need utm_content here as well.

version = firefox_android.latest_version(channel)
builds = android_builds(channel, builds)

utm_params = ctx["request"].GET.copy()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Aren't those query_params and not just the UTM ones? Query params are a bit more than just the UTM params.

@stevejalim
Copy link
Copy Markdown
Contributor

stevejalim commented Apr 9, 2026

@bluewave41 This is coming together well - thanks!

When testing just now, using the links in the description, I noticed that the utm_content queryparam pair is not being escaped into the referrrer for the Play Store URL. Is that deliberate? If not, I'm assuming we should be carrying it over.

@willdurand Can you advise here on what the expected behaviour needs to be, please?

EDIT: ah, Will's addressed that in a separate comment

@stevejalim
Copy link
Copy Markdown
Contributor

If this changeset needs to go into the FXC codebase, please add the WMO and FXC label.

I think it does, right?

@willdurand Yes it will, but first we'll get it working on www.m.o before we also support on www.firefox.com because right now RTAMO still goes only to www.m.o. yeah?

return (
self.store_url
+ "&referrer="
+ quote(f"utm_source={utm_params['utm_source']}&utm_medium={utm_params['utm_medium']}&utm_campaign={utm_params['utm_campaign']}")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we shouldn't assume that the utm_params have these keys. We should concatenate all the key/value pairs in utm_params instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants