Skip to content

Add FXIOS-12301 [Context ID] Integrate with rust context id rotation component #27195

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions firefox-ios/Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1030,8 +1030,8 @@
8A93F87229D3A5AD004159D9 /* BrowserCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A93F87129D3A5AD004159D9 /* BrowserCoordinator.swift */; };
8A93F87429D3A5C1004159D9 /* LaunchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A93F87329D3A5C1004159D9 /* LaunchCoordinator.swift */; };
8A94418A2CE3E190007FF4E5 /* MockSearchEngineManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9441892CE3E190007FF4E5 /* MockSearchEngineManager.swift */; };
8A95FF642B1E969E00AC303D /* TelemetryContextualIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF632B1E969E00AC303D /* TelemetryContextualIdentifier.swift */; };
8A95FF672B1E97A800AC303D /* TelemetryContextualIdentifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */; };
8A95FF642B1E969E00AC303D /* ContextIDManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF632B1E969E00AC303D /* ContextIDManager.swift */; };
8A95FF672B1E97A800AC303D /* ContextIDManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF652B1E977E00AC303D /* ContextIDManagerTests.swift */; };
8A967E422D30419100B1017D /* TelemetryDebugMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A967E412D30418A00B1017D /* TelemetryDebugMessage.swift */; };
8A96C4BB28F9E7B300B75884 /* XCTestCaseRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */; };
8A97E6EA2C58487900F94793 /* AddressLocaleFeatureValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A97E6E92C58487900F94793 /* AddressLocaleFeatureValidator.swift */; };
Expand Down Expand Up @@ -8433,8 +8433,8 @@
8A93F87129D3A5AD004159D9 /* BrowserCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserCoordinator.swift; sourceTree = "<group>"; };
8A93F87329D3A5C1004159D9 /* LaunchCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchCoordinator.swift; sourceTree = "<group>"; };
8A9441892CE3E190007FF4E5 /* MockSearchEngineManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSearchEngineManager.swift; sourceTree = "<group>"; };
8A95FF632B1E969E00AC303D /* TelemetryContextualIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryContextualIdentifier.swift; sourceTree = "<group>"; };
8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryContextualIdentifierTests.swift; sourceTree = "<group>"; };
8A95FF632B1E969E00AC303D /* ContextIDManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextIDManager.swift; sourceTree = "<group>"; };
8A95FF652B1E977E00AC303D /* ContextIDManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextIDManagerTests.swift; sourceTree = "<group>"; };
8A967E412D30418A00B1017D /* TelemetryDebugMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryDebugMessage.swift; sourceTree = "<group>"; };
8A96C4B828F9DD8700B75884 /* ThemableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemableTests.swift; sourceTree = "<group>"; };
8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestCaseRootViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -14945,7 +14945,7 @@
8A04136828258DF600D20B10 /* SponsoredTileTelemetry.swift */,
8AD08D1427E9198E00B8E907 /* TabsTelemetry.swift */,
8A44F20D2B585E1F0016BC81 /* HomepageTelemetry.swift */,
8A95FF632B1E969E00AC303D /* TelemetryContextualIdentifier.swift */,
8A95FF632B1E969E00AC303D /* ContextIDManager.swift */,
EBF47E6F1F7979DF00899189 /* TelemetryWrapper.swift */,
8CA4E80C2D22C066007207C1 /* GleanUsageReporting.swift */,
1DD4B26D2CA4D09100B51945 /* TabErrorTelemetryHelper.swift */,
Expand Down Expand Up @@ -15291,7 +15291,7 @@
21D8EA942ABE0511003FF16E /* TabTray */,
8A6E13972A71BA4E00A88FA8 /* TabWebViewTests.swift */,
8AC225632B6D3F9600CDA7FD /* Telemetry */,
8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */,
8A95FF652B1E977E00AC303D /* ContextIDManagerTests.swift */,
8AA6ADB42742B567004EEE23 /* TelemetryWrapperTests.swift */,
0B7B053C2D647D00007FD7AC /* TemporaryDocumentTests.swift */,
0BA8964A1A250E6500C1010C /* TestBookmarks.swift */,
Expand Down Expand Up @@ -17914,7 +17914,7 @@
E1442FD1294782D9003680B0 /* UIModalPresentationStyle+Photon.swift in Sources */,
21EEAA1D2D4005DE00595119 /* NativeErrorPageFeatureFlag.swift in Sources */,
5A32C2B62AD8517200A9B5A4 /* MetricKitWrapper.swift in Sources */,
8A95FF642B1E969E00AC303D /* TelemetryContextualIdentifier.swift in Sources */,
8A95FF642B1E969E00AC303D /* ContextIDManager.swift in Sources */,
D3FEC38D1AC4B42F00494F45 /* AutocompleteTextField.swift in Sources */,
F89453A92DA71D530022BA3F /* DeleteLoginsKeysSetting.swift in Sources */,
8A19ACAE2A329058001C2147 /* PasswordManagerSetting.swift in Sources */,
Expand Down Expand Up @@ -18509,7 +18509,7 @@
8A7A26E129D4785900EA76F1 /* MockRouter.swift in Sources */,
965C3C96293431FC006499ED /* MockLaunchSessionProvider.swift in Sources */,
C869915628917803007ACC5C /* WallpaperJSONTestProvider.swift in Sources */,
8A95FF672B1E97A800AC303D /* TelemetryContextualIdentifierTests.swift in Sources */,
8A95FF672B1E97A800AC303D /* ContextIDManagerTests.swift in Sources */,
8A4B66422D1DC96A008C5B64 /* ContextMenuConfigurationTests.swift in Sources */,
965C3C9829343445006499ED /* MockAppSessionManager.swift in Sources */,
8AFCE50929DE136300B1B253 /* MockLaunchFinishedLoadingDelegate.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion firefox-ios/Client/Application/AppLaunchUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ final class AppLaunchUtil {
} else {
// If ToS are not accepted, we still need to setup the Contextual Identifier for
// the Unified Ads Sponsored tiles
TelemetryContextualIdentifier.setupContextId(isGleanMetricsAllowed: false)
ContextIDManager.setup(isGleanMetricsAllowed: false, isTesting: AppConstants.isRunningTest)
}
} else {
logger.setup(sendCrashReports: sendCrashReports)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,7 @@ class UnifiedAdsProvider: URLCaching, UnifiedAdsProviderInterface, FeatureFlagga
return nil
}

guard let contextId = TelemetryContextualIdentifier.contextId else {
logger.log("No context id: \(String(describing: TelemetryContextualIdentifier.contextId))",
level: .warning,
category: .legacyHomepage)
guard let contextId = ContextIDManager.shared.getContextID() else {
return nil
}

Expand Down
95 changes: 95 additions & 0 deletions firefox-ios/Client/Telemetry/ContextIDManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Foundation
import Glean
import MozillaAppServices

// Contextual identifier used for the sponsored tiles in top sites and the suggestions in the search view
class ContextIDManager {
private var contextIDComponent: ContextIdComponent?

static let shared = ContextIDManager()

static func setup(isGleanMetricsAllowed allowed: Bool, isTesting: Bool) {
shared.contextIDComponent = shared.setupContextId(isGleanMetricsAllowed: allowed, isTesting: isTesting)
}

private struct ContextIDStorage {
enum UserDefaultsKey: String {
case keyContextId = "com.moz.contextId.key"
case keyContextIdCreationTimestamp
}

static var contextID: String? {
get { UserDefaults.standard.object(forKey: UserDefaultsKey.keyContextId.rawValue) as? String }
set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.keyContextId.rawValue) }
}

static var contextIdCreationTimeStamp: Int64? {
get { UserDefaults.standard.object(forKey: UserDefaultsKey.keyContextId.rawValue) as? Int64 }
set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.keyContextId.rawValue) }
}

static func setContextID(_ contextID: String?, creationTimestamp: Int64?) {
ContextIDStorage.contextID = contextID
ContextIDStorage.contextIdCreationTimeStamp = creationTimestamp
}
}

func getContextID() -> String? {
do {
// TODO: Set up rotation as a feature flag
return try contextIDComponent?.request(rotationDaysInS: 0)
} catch {
// handle error
return nil
}
}

fileprivate func setContextID(_ contextID: String?, creationTimestamp: Int64?) {
ContextIDStorage.setContextID(contextID, creationTimestamp: creationTimestamp)
}

private func setupContextId(isGleanMetricsAllowed allowed: Bool, isTesting: Bool) -> ContextIdComponent? {
do {
return try ContextIdComponent(
initContextId: ContextIDStorage.contextID ?? "",
creationTimestampS: ContextIDStorage.contextIdCreationTimeStamp ?? 0,
runningInTestAutomation: isTesting,
callback: ContextIDRotationHandler(isGleanMetricsAllowed: allowed)
)
} catch {
// catch error
return nil
}
}
}

class ContextIDRotationHandler: ContextIdCallback {
private let isGleanMetricsAllowed: Bool

init(isGleanMetricsAllowed: Bool) {
self.isGleanMetricsAllowed = isGleanMetricsAllowed
}

func persist(contextId: String, creationDate: Int64) {
ContextIDManager.shared.setContextID(contextId, creationTimestamp: creationDate)
guard isGleanMetricsAllowed else { return }
guard let uuid = UUID(uuidString: contextId) else {
// log error
return
}
GleanMetrics.TopSites.contextId.set(uuid)
}

func rotated(oldContextId: String) {
// NO-OP
Copy link
Contributor

Choose a reason for hiding this comment

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

I see GleanPings.contextIdDeletionRequest.submit(); commented in there. Will that ContextIDManager support deleting the context id? I had some discussion with Mike Conley (summary noted in https://mozilla-hub.atlassian.net/browse/FXIOS-11783), and we're not deleting the local context_id even when toggling off interaction data but we should. Happy to discuss in coffee chat to give more context (pun intended 😄 )

/*
GleanPings.contextIdDeletionRequest.setEnabled(true);
Glean.contextualServices.contextId.set(oldContextId);
GleanPings.contextIdDeletionRequest.submit();
*/
}
}

This file was deleted.

6 changes: 3 additions & 3 deletions firefox-ios/Client/Telemetry/TelemetryWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable {

self.searchEnginesManager = searchEnginesManager

TelemetryContextualIdentifier.setupContextId()
ContextIDManager.setup(isGleanMetricsAllowed: true, isTesting: AppConstants.isRunningTes)

// Register an observer to record settings and other metrics that are more appropriate to
// record on going to background rather than during initialization.
Expand Down Expand Up @@ -1794,7 +1794,7 @@ extension TelemetryWrapper {

// MARK: - FX Suggest
case(.action, .tap, .fxSuggest, _, let extras):
guard let contextIdString = TelemetryContextualIdentifier.contextId,
guard let contextIdString = ContextIDManager.shared.getContextID(),
let contextId = UUID(uuidString: contextIdString),
let telemetryInfo = extras?[EventValue.fxSuggestionTelemetryInfo.rawValue] as? RustFirefoxSuggestionTelemetryInfo,
let position = extras?[EventValue.fxSuggestionPosition.rawValue] as? Int else {
Expand Down Expand Up @@ -1829,7 +1829,7 @@ extension TelemetryWrapper {
GleanMetrics.Pings.shared.fxSuggest.submit()

case(.action, .view, .fxSuggest, _, let extras):
guard let contextIdString = TelemetryContextualIdentifier.contextId,
guard let contextIdString = ContextIDManager.shared.getContextID(),
let contextId = UUID(uuidString: contextIdString),
let telemetryInfo = extras?[EventValue.fxSuggestionTelemetryInfo.rawValue] as? RustFirefoxSuggestionTelemetryInfo,
let position = extras?[EventValue.fxSuggestionPosition.rawValue] as? Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class BrowserViewControllerTests: XCTestCase, StoreTestUtility {
override func setUp() {
super.setUp()
DependencyHelperMock().bootstrapDependencies()
TelemetryContextualIdentifier.setupContextId()
// Due to changes allow certain custom pings to implement their own opt-out
// independent of Glean, custom pings may need to be registered manually in
// tests in order to put them in a state in which they can collect data.
Expand All @@ -39,7 +38,6 @@ class BrowserViewControllerTests: XCTestCase, StoreTestUtility {
}

override func tearDown() {
TelemetryContextualIdentifier.clearUserDefaults()
profile.shutdown()
profile = nil
tabManager = nil
Expand All @@ -56,10 +54,6 @@ class BrowserViewControllerTests: XCTestCase, StoreTestUtility {

GleanMetrics.Pings.shared.fxSuggest.testBeforeNextSubmit { _ in
XCTAssertEqual(GleanMetrics.FxSuggest.pingType.testGetValue(), "fxsuggest-impression")
XCTAssertEqual(
GleanMetrics.FxSuggest.contextId.testGetValue()?.uuidString,
TelemetryContextualIdentifier.contextId
)
XCTAssertEqual(GleanMetrics.FxSuggest.isClicked.testGetValue(), false)
XCTAssertEqual(GleanMetrics.FxSuggest.position.testGetValue(), 3)
XCTAssertEqual(GleanMetrics.FxSuggest.blockId.testGetValue(), 1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

@testable import Client

import Glean
import XCTest

class ContextIDManagerTests: XCTestCase {
override func setUp() {
super.setUp()
DependencyHelperMock().bootstrapDependencies()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,16 @@ class SponsoredTileTelemetryTests: XCTestCase {
override func setUp() {
super.setUp()
gleanWrapper = MockGleanWrapper()
clearTest()
}

override func tearDown() {
clearTest()
gleanWrapper = nil
super.tearDown()
}

// MARK: Impression

func testImpressionTopSite() {
TelemetryContextualIdentifier.setupContextId()
let contile = ContileProviderMock.defaultSuccessData[0]
let topSite = Site.createSponsoredSite(fromContile: contile)

Expand All @@ -49,7 +46,6 @@ class SponsoredTileTelemetryTests: XCTestCase {
// MARK: Click

func testClickTopSite() {
TelemetryContextualIdentifier.setupContextId()
let contile = ContileProviderMock.defaultSuccessData[1]
let topSite = Site.createSponsoredSite(fromContile: contile)

Expand All @@ -74,8 +70,4 @@ class SponsoredTileTelemetryTests: XCTestCase {
func createSubject() -> SponsoredTileTelemetry {
return DefaultSponsoredTileTelemetry(gleanWrapper: gleanWrapper)
}

func clearTest() {
TelemetryContextualIdentifier.clearUserDefaults()
}
}

This file was deleted.