fix(forms): validate required bridge message fields (MAGE-484)#437
fix(forms): validate required bridge message fields (MAGE-484)#437evan-masseau wants to merge 5 commits intofeat/form-hooks-and-deep-linkfrom
Conversation
Add a requireString() extension on JSONObject that throws IllegalArgumentException when a required field is missing or empty. The existing catch-and-log in KlaviyoNativeBridge.postMessage() prevents malformed events from propagating to the host app. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The empty-android-route test verified that neither the lifecycle handler nor DeepLinking were invoked, but didn't verify the warning log fires. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit edbe617. Configure here.
sdk/forms/src/main/java/com/klaviyo/forms/bridge/NativeBridgeMessage.kt
Outdated
Show resolved
Hide resolved
sdk/forms/src/test/java/com/klaviyo/forms/bridge/KlaviyoNativeBridgeTest.kt
Outdated
Show resolved
Hide resolved
… overlays FormDisappeared now uses optString instead of requireString so that dismiss() always fires even if the JS bridge sends malformed data. Also fixes test assertions to verify log level rather than exact message content, per project guidelines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| keyName<FormWillAppear>() -> FormWillAppear( | ||
| formId = jsonData.optString("formId"), | ||
| formName = jsonData.optString("formName") | ||
| formId = jsonData.requireString("formId"), |
There was a problem hiding this comment.
Hm did not consider that this would mean the whole formWillAppear event would get blocked. I think the validation on ID and name etc should only be applied to the lifecycle even broadcast, so I guess it can't go in the json parsing but the LifecycleEvent construction instead
…nt dispatch Parsing now always uses optString() with empty defaults so SDK-internal actions (present overlay, dismiss, handle deep links) are never blocked by missing metadata. Validation of formId/formName/buttonLabel is done in KlaviyoNativeBridge's show/close/deepLink methods right before dispatching FormLifecycleEvent to the host app callback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| keyName<FormWillAppear>() -> FormWillAppear( | ||
| formId = jsonData.optString("formId"), | ||
| formName = jsonData.optString("formName") | ||
| formId = jsonData.optString("formId", ""), |
There was a problem hiding this comment.
you don't need to change this... its already the behavior of optString without a second arg
| verify(exactly = 0) { mockLifecycleHandler.onFormLifecycleEvent(any()) } | ||
| verify { | ||
| spyLog.warning( | ||
| "FormWillAppear missing required fields, skipping lifecycle callback" |
There was a problem hiding this comment.
don't verify full logs strings, just levels is fine.
…e level-only log assertions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Summary
requireString()extension onJSONObjectthat throwsIllegalArgumentExceptionwhen a required field (formId,formName,buttonLabel) is missing or empty from a bridge messagecatchinKlaviyoNativeBridge.postMessage()logs the error and prevents malformed events from reaching the host appPart of MAGE-484
Motivated by feedback from Dan on the Flutter SDK PR (#37) where missing fields from the JS bridge were silently creating events with empty strings, which could confuse host app developers.
Test plan
./gradlew :sdk:forms:testDebugUnitTestpasses (137 tests, 0 failures)./gradlew ktlintCheckpasses🤖 Generated with Claude Code