Skip to content

feat: UserAttributeListener onboarding and BaseAttributeListener for kits#688

Closed
denischilik wants to merge 10 commits intoworkstation/6.0-Releasefrom
feat/migrate-onset-user-attribute
Closed

feat: UserAttributeListener onboarding and BaseAttributeListener for kits#688
denischilik wants to merge 10 commits intoworkstation/6.0-Releasefrom
feat/migrate-onset-user-attribute

Conversation

@denischilik
Copy link
Copy Markdown

@denischilik denischilik commented Mar 31, 2026

Background

Aligns kit code with UserAttributeListener and BaseAttributeListener so user-attribute handling uses the non-deprecated entry points (onSetUserAttribute, onRemoveUserAttribute) and shared supportsAttributeLists() behavior from kit-base.

What Has Changed

  • Introduces BaseAttributeListener and routes supportsAttributeLists() through kit-base (KitManagerImpl) once per provider
  • Removes removeUserAttribute from AttributeListener; kits implement removal via onRemoveUserAttribute (with kit-specific helpers where needed)
  • Updates kits (Braze, AppsFlyer, Singular, and others touched in this branch) for consistent onSetUserAttribute / onRemoveUserAttribute signatures and null-safe Java interop
  • Adjusts kit-base tests and test kits for the new listener shape

Screenshots/Video

N/A

Checklist

  • I have performed a self-review of my own code.
  • I have made corresponding changes to the documentation.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have tested this locally.

Additional Notes

Merge base: feat/remove-attribute-listener (stacked PR).

Introduce KitIntegration.BaseAttributeListener as the common superinterface
for AttributeListener and UserAttributeListener. Document it as a temporary
contract to enable incremental migration and smaller PRs.

Made-with: Cursor
Resolve via BaseAttributeListener before AttributeListener/UserAttributeListener
branches in onUserAttributesReceived and setUserAttribute(list).

Made-with: Cursor
UserAttributeListener.onRemoveUserAttribute now forwards to the existing
AttributeListener.removeUserAttribute implementation for braze-38 through braze-41.

Made-with: Cursor
Declare onRemoveUserAttribute on KitIntegration.BaseAttributeListener so
AttributeListener and UserAttributeListener implementors must implement it.
KitManagerImpl now dispatches removal once via BaseAttributeListener.

AttributeListener-only kits delegate onRemoveUserAttribute to removeUserAttribute;
AttributeListenerTestKit updated accordingly.

Made-with: Cursor
Drop void removeUserAttribute(String) from KitIntegration.AttributeListener.
Kits keep the same logic as private-style helpers (fun removeUserAttribute
without override) and still satisfy BaseAttributeListener via
onRemoveUserAttribute. AdobeKitBase implements onRemoveUserAttribute by
delegating to removeUserAttribute.

Made-with: Cursor
Move kit-specific handling from removed helper methods into
BaseAttributeListener.onRemoveUserAttribute implementations.
Rename AttributeListenerTestKit callback to removeUserAttributeListener.
Fix Braze custom-key branch for Kotlin smart-cast in Braze callback.

Made-with: Cursor
Document intentional empty overrides in Adobe, AppsFlyer, Branch, Braze,
and Kochava kits to satisfy SonarQube analysis.

Made-with: Cursor
Replace if/else chain with when for standard vs custom user attribute removal; behavior unchanged across braze 38-41 kits.

Made-with: Cursor
…arams, naming)

- SingularKit: key/value/user naming, null checks for Java interop
- AppsFlyerKit, Braze AppboyKit: consistent UserAttributeListener overrides

Made-with: Cursor
@denischilik denischilik requested a review from a team as a code owner March 31, 2026 14:09
@cursor
Copy link
Copy Markdown

cursor bot commented Mar 31, 2026

PR Summary

Medium Risk
Touches core kit forwarding (KitManagerImpl) and listener interfaces used by many kits, so regressions could drop or mis-route user attribute updates/removals if any kit doesn’t implement the new callbacks correctly.

Overview
Unifies kit user-attribute listener contracts. Adds BaseAttributeListener to share supportsAttributeLists() and onRemoveUserAttribute(...), updates AttributeListener/UserAttributeListener to extend it, and removes the old removeUserAttribute path.

Updates forwarding logic in KitManagerImpl. supportsAttributeLists() is now fetched once per provider and used for both legacy and new listener types, and removeUserAttribute dispatch is consolidated to a single BaseAttributeListener.onRemoveUserAttribute(...) call.

Migrates kits and tests to the new callbacks. Multiple kits now implement onRemoveUserAttribute(key, user) (and some add null/type guards in onSetUserAttribute), while android tests/test-kits are adjusted to validate the new removal listener shape.

Written by Cursor Bugbot for commit 643c563. This will update automatically on new commits. Configure here.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
67.7% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

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 1 potential issue.

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.

if (key == null || value == null || value !is String) {
return
}
setUserAttribute(key, value)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Double user attribute dispatch for dual-interface kits

High Severity

For kits implementing both AttributeListener and UserAttributeListener (Braze, Singular), KitManagerImpl.setUserAttribute dispatches through both AttributeListener.setUserAttribute() and UserAttributeListener.onSetUserAttribute(). Previously onSetUserAttribute was a no-op, but it now delegates to setUserAttribute(), causing every user attribute to be set twice on the downstream SDK. This doubles Braze API calls and fires duplicate Singular events for AGE/GENDER attributes. The same issue applies to all four Braze kit versions (38–41).

Additional Locations (2)
Fix in Cursor Fix in Web

@denischilik denischilik deleted the feat/migrate-onset-user-attribute branch March 31, 2026 14:22
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