Skip to content

Commit 43c6be6

Browse files
authored
Bugfix FXIOS-9490 [Microsurvey] Using default action causes issues opening survey (mozilla-mobile#21019)
1 parent f4ade4d commit 43c6be6

File tree

4 files changed

+64
-26
lines changed

4 files changed

+64
-26
lines changed

firefox-ios/Client/Experiments/Messaging/GleanPlumbMessage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct GleanPlumbMessage {
4141
/// The action URL as resolved by the Nimbus Messaging component.
4242
///
4343
/// Embedding apps should not read from this directly.
44-
let action: String
44+
let action: String?
4545

4646
/// The conditions that need to be satisfied for a message to be considered eligible to present.
4747
///

firefox-ios/Client/Experiments/Messaging/GleanPlumbMessageManager.swift

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,33 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {
194194
func onMessagePressed(_ message: GleanPlumbMessage, window: WindowUUID?, shouldExpire: Bool = true) {
195195
messagingStore.onMessagePressed(message, shouldExpire: shouldExpire)
196196

197-
guard let helper = createMessagingHelper.createNimbusMessagingHelper() else { return }
197+
var extras = baseTelemetryExtras(using: message)
198+
if let action = message.action {
199+
if let uuid = handleLinkAction(for: message, action: action, window: window) {
200+
extras[MessagingKey.actionUUID.rawValue] = uuid
201+
}
202+
}
203+
204+
TelemetryWrapper.recordEvent(category: .action,
205+
method: .tap,
206+
object: .messaging,
207+
value: .messageInteracted,
208+
extras: extras)
209+
}
198210

199-
guard let (uuid, urlToOpen) = try? self.generateUuidAndFormatAction(for: message, with: helper) else {
211+
private func handleLinkAction(
212+
for message: GleanPlumbMessage,
213+
action: String,
214+
window: WindowUUID?
215+
) -> String? {
216+
guard let helper = createMessagingHelper.createNimbusMessagingHelper() else { return nil }
217+
guard let (uuid, urlToOpen) = try? self.generateUuidAndFormatAction(
218+
for: action,
219+
with: message.data.actionParams,
220+
with: helper
221+
) else {
200222
self.onMalformedMessage(id: message.id, surface: message.surface)
201-
return
223+
return nil
202224
}
203225

204226
// With our well-formed URL, we can handle the action here.
@@ -207,25 +229,16 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {
207229
} else {
208230
applicationHelper.open(urlToOpen)
209231
}
210-
211-
var extras = baseTelemetryExtras(using: message)
212-
if let uuid = uuid {
213-
extras[MessagingKey.actionUUID.rawValue] = uuid
214-
}
215-
TelemetryWrapper.recordEvent(category: .action,
216-
method: .tap,
217-
object: .messaging,
218-
value: .messageInteracted,
219-
extras: extras)
232+
return uuid
220233
}
221234

222-
private func generateUuidAndFormatAction(for message: GleanPlumbMessage,
235+
private func generateUuidAndFormatAction(for action: String,
236+
with actionParams: [String: String],
223237
with helper: NimbusMessagingHelperProtocol) throws -> (String?, URL) {
224238
// Make substitutions where they're needed.
225-
let actionTemplate = message.action
239+
let actionTemplate = action
226240
var uuid = helper.getUuid(template: actionTemplate)
227241
let action = helper.stringFormat(template: actionTemplate, uuid: uuid)
228-
let actionParams = message.data.actionParams
229242

230243
let urlString: String = action.hasPrefix("://") ? deepLinkScheme + action : action
231244
guard var components = URLComponents(string: urlString) else {
@@ -325,16 +338,19 @@ class GleanPlumbMessageManager: GleanPlumbMessageManagerProtocol {
325338
message: MessageData,
326339
lookupTables: Messaging
327340
) -> Result<GleanPlumbMessage, CreateMessageError> {
328-
var action: String
341+
var action: String?
329342
if !message.isControl {
330343
// Guard against a message with a blank `text` property.
331344
guard !message.text.isEmpty else { return .failure(.malformed) }
332345

333-
// The message action should be either from the lookup table OR a URL.
334-
guard let safeAction = sanitizeAction(message.action, table: lookupTables.actions) else {
335-
return .failure(.malformed)
346+
// Message action can be null. If not null,
347+
// the message action should be either from the lookup table OR a URL.
348+
if let messageAction = message.action {
349+
guard let safeAction = sanitizeAction(messageAction, table: lookupTables.actions) else {
350+
return .failure(.malformed)
351+
}
352+
action = safeAction
336353
}
337-
action = safeAction
338354
} else {
339355
action = "CONTROL_ACTION"
340356
}

firefox-ios/firefox-ios-tests/Tests/ClientTests/Messaging/GleanPlumbMessageManagerTests.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,19 @@ class GleanPlumbMessageManagerTests: XCTestCase {
240240
XCTAssertEqual(hardcodedNimbusFeatures.getExposureCount(featureId: "messaging"), 2)
241241
}
242242

243+
func testManagerGetMessages_happyPath_withNoAction() throws {
244+
let expectedId = "infoCard"
245+
let messages = [
246+
createMessage(messageId: "infoCard-notyet", action: nil, surface: .newTabCard, trigger: ["true", "false"]),
247+
createMessage(messageId: expectedId, action: nil, surface: .newTabCard, trigger: ["true", "true"])
248+
]
249+
guard let observed = subject.getNextMessage(for: .newTabCard, from: messages) else {
250+
XCTFail("Expected to retrieve message")
251+
return
252+
}
253+
XCTAssertEqual(observed.id, expectedId)
254+
}
255+
243256
func testManagerOnMessageDisplayed() {
244257
let message = createMessage(messageId: messageId)
245258
subject.onMessageDisplayed(message)
@@ -357,6 +370,16 @@ class GleanPlumbMessageManagerTests: XCTestCase {
357370
testEventMetricRecordingSuccess(metric: GleanMetrics.Messaging.malformed)
358371
}
359372

373+
func testManagerOnMessagePressed_withNoAction() {
374+
let message = createMessage(messageId: messageId, action: nil)
375+
subject.onMessagePressed(message, window: nil)
376+
let messageMetadata = messagingStore.getMessageMetadata(messageId: messageId)
377+
XCTAssertTrue(messageMetadata.isExpired)
378+
XCTAssertEqual(applicationHelper.openURLCalled, 0)
379+
XCTAssertNil(applicationHelper.lastOpenURL)
380+
testEventMetricRecordingSuccess(metric: GleanMetrics.Messaging.clicked)
381+
}
382+
360383
func testManagerOnMessageDismissed() {
361384
let message = createMessage(messageId: messageId)
362385
subject.onMessageDismissed(message)
@@ -369,7 +392,7 @@ class GleanPlumbMessageManagerTests: XCTestCase {
369392
// MARK: - Helper function
370393

371394
private func createMessage(messageId: String,
372-
action: String = "MAKE_DEFAULT_BROWSER",
395+
action: String? = "MAKE_DEFAULT_BROWSER",
373396
actionParams: [String: String] = [:],
374397
surface: MessageSurfaceId = .newTabCard,
375398
trigger: [String] = ["true"],

firefox-ios/nimbus-features/messaging/messaging.fml.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,10 @@ objects:
103103
action:
104104
# We would like this to be of ActionName type, but it accepts https:// URLs
105105
# so changing this would be a breaking change.
106-
type: ActionName
106+
type: Option<ActionName>
107107
description: >
108108
The name of a deeplink URL to be opened if the button is clicked.
109-
# This should never be defaulted.
110-
default: OPEN_URL
109+
default: null
111110
action-params:
112111
type: Map<String, String>
113112
description: Query parameters appended to the deeplink action URL

0 commit comments

Comments
 (0)