Skip to content

fix(forms): validate required bridge message fields (MAGE-484)#437

Draft
evan-masseau wants to merge 5 commits intofeat/form-hooks-and-deep-linkfrom
ecm/lifecycle-hook-parse-errors
Draft

fix(forms): validate required bridge message fields (MAGE-484)#437
evan-masseau wants to merge 5 commits intofeat/form-hooks-and-deep-linkfrom
ecm/lifecycle-hook-parse-errors

Conversation

@evan-masseau
Copy link
Copy Markdown
Contributor

Summary

  • Add requireString() extension on JSONObject that throws IllegalArgumentException when a required field (formId, formName, buttonLabel) is missing or empty from a bridge message
  • The existing catch in KlaviyoNativeBridge.postMessage() logs the error and prevents malformed events from reaching the host app
  • Updated all tests to supply required fields and added new tests for missing-field error paths

Part 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:testDebugUnitTest passes (137 tests, 0 failures)
  • ./gradlew ktlintCheck passes
  • Verify on device that forms still render and dismiss correctly
  • Verify deep link CTA events still fire with proper context

🤖 Generated with Claude Code

evan-masseau and others added 2 commits April 10, 2026 17:21
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>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ 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.

… 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"),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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", ""),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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>
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.

1 participant