Skip to content

Commit 9d07e1c

Browse files
authored
feat: inline user attribute removal in onRemoveUserAttribute (#687)
* Add BaseAttributeListener for shared supportsAttributeLists() 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 * KitManagerImpl: call supportsAttributeLists once per provider Resolve via BaseAttributeListener before AttributeListener/UserAttributeListener branches in onUserAttributesReceived and setUserAttribute(list). Made-with: Cursor * Braze kit: delegate onRemoveUserAttribute to removeUserAttribute UserAttributeListener.onRemoveUserAttribute now forwards to the existing AttributeListener.removeUserAttribute implementation for braze-38 through braze-41. Made-with: Cursor * Move onRemoveUserAttribute to BaseAttributeListener 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 * Remove removeUserAttribute from AttributeListener; keep kit helpers 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 * Inline remove-user-attribute logic into onRemoveUserAttribute 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 * chore: add no-op comments for Sonar empty-method rules Document intentional empty overrides in Adobe, AppsFlyer, Branch, Braze, and Kochava kits to satisfy SonarQube analysis. Made-with: Cursor * chore(kochava): Sonar no-op comment on onRemoveUserAttribute Made-with: Cursor * refactor(braze): use when in onRemoveUserAttribute success path Replace if/else chain with when for standard vs custom user attribute removal; behavior unchanged across braze 38-41 kits. Made-with: Cursor
1 parent 61fcacf commit 9d07e1c

18 files changed

Lines changed: 234 additions & 185 deletions

File tree

android-kit-base/src/androidTest/kotlin/com/mparticle/kits/DataplanBlockingUserTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class DataplanBlockingUserTests : BaseKitOptionsTest() {
152152
// make sure these are the attributes that are being removed
153153
count++
154154
}
155-
attributeListenerKitKit.removeUserAttribute = {
155+
attributeListenerKitKit.removeUserAttributeListener = {
156156
assertTrue(allowedAttributes.containsKey(it))
157157
assertFalse(blockedAttributes.containsKey(it))
158158
count++

android-kit-base/src/androidTest/kotlin/com/mparticle/kits/testkits/AttributeListenerTestKit.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.mparticle.kits.testkits
22

33
import com.mparticle.MParticle
4+
import com.mparticle.kits.FilteredMParticleUser
45
import com.mparticle.kits.KitIntegration.AttributeListener
56
import com.mparticle.kits.KitIntegration.LogoutListener
67
import com.mparticle.kits.ReportingMessage
@@ -15,7 +16,7 @@ open class AttributeListenerTestKit :
1516
var supportsAttributeLists: (() -> Boolean)? = null
1617
var setAllUserAttributes: ((userAttributes: Map<String, String>?, userAttributeLists: Map<String, List<String>>?) -> Unit)? =
1718
null
18-
var removeUserAttribute: ((key: String?) -> Unit)? = null
19+
var removeUserAttributeListener: ((key: String?) -> Unit)? = null
1920
var setUserIdentity: ((identityType: MParticle.IdentityType?, identity: String?) -> Unit)? =
2021
null
2122
var removeUserIdentity: ((identityType: MParticle.IdentityType?) -> Unit)? = null
@@ -61,8 +62,11 @@ open class AttributeListenerTestKit :
6162
onIdentityReceived?.invoke(identityType, null)
6263
}
6364

64-
override fun removeUserAttribute(key: String) {
65-
removeUserAttribute?.invoke(key)
65+
override fun onRemoveUserAttribute(
66+
key: String,
67+
user: FilteredMParticleUser,
68+
) {
69+
removeUserAttributeListener?.invoke(key)
6670
onAttributeReceived?.invoke(key, null)
6771
}
6872

android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -361,29 +361,41 @@ public interface LogoutListener {
361361
}
362362

363363
/**
364-
* Kits should implement this interface when their underlying service has the notion
365-
* of a user with attributes.
364+
* Temporary shared contract used while {@link AttributeListener} behavior is migrated to
365+
* {@link UserAttributeListener}. Factoring out common API surface lets the SDK land incremental changes
366+
* and smaller pull requests instead of a single large refactor.
367+
* <p>
368+
* Kits implement {@link AttributeListener} and/or {@link UserAttributeListener}; they do not implement this
369+
* type directly.
366370
*/
367371
@Deprecated
368-
public interface AttributeListener {
369-
370-
void setUserAttribute(String attributeKey, String attributeValue);
371-
372-
void setUserAttributeList(String attributeKey, List<String> attributeValueList);
372+
public interface BaseAttributeListener {
373373

374374
/**
375-
* Indicate to the mParticle Kit framework if this AttributeListener supports attribute-values as lists.
375+
* Indicate to the mParticle Kit framework if this listener supports attribute-values as lists.
376376
* <p>
377-
* If an AttributeListener returns false, the setUserAttributeList method will never be called. Instead, setUserAttribute
378-
* will be called with the attribute-value lists combined as a csv.
377+
* If false, list-specific APIs are not used; values are passed via scalar/csv paths instead.
379378
*
380-
* @return true if this AttributeListener supports attribute values as lists.
379+
* @return true if this listener supports attribute values as lists.
381380
*/
382381
boolean supportsAttributeLists();
383382

384-
void setAllUserAttributes(Map<String, String> userAttributes, Map<String, List<String>> userAttributeLists);
383+
/**
384+
* Called when a user attribute is removed for the current user.
385+
*
386+
* @param key attribute key
387+
* @param user filtered user context for this kit
388+
*/
389+
void onRemoveUserAttribute(String key, FilteredMParticleUser user);
390+
}
385391

386-
void removeUserAttribute(String key);
392+
public interface AttributeListener extends BaseAttributeListener {
393+
394+
void setUserAttribute(String attributeKey, String attributeValue);
395+
396+
void setUserAttributeList(String attributeKey, List<String> attributeValueList);
397+
398+
void setAllUserAttributes(Map<String, String> userAttributes, Map<String, List<String>> userAttributeLists);
387399

388400
void setUserIdentity(MParticle.IdentityType identityType, String identity);
389401

@@ -540,12 +552,10 @@ public interface IdentityListener {
540552

541553
}
542554

543-
public interface UserAttributeListener {
555+
public interface UserAttributeListener extends BaseAttributeListener {
544556

545557
void onIncrementUserAttribute(String key, Number incrementedBy, String value, FilteredMParticleUser user);
546558

547-
void onRemoveUserAttribute(String key, FilteredMParticleUser user);
548-
549559
void onSetUserAttribute(String key, Object value, FilteredMParticleUser user);
550560

551561
void onSetUserTag(String key, FilteredMParticleUser user);
@@ -554,8 +564,6 @@ public interface UserAttributeListener {
554564

555565
void onSetAllUserAttributes(Map<String, String> userAttributes, Map<String, List<String>> userAttributeLists, FilteredMParticleUser user);
556566

557-
boolean supportsAttributeLists();
558-
559567
void onConsentStateUpdated(ConsentState oldState, ConsentState newState, FilteredMParticleUser user);
560568
}
561569

android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,9 @@ public void onUserAttributesReceived(Map<String, String> userAttributes, Map<Str
605605
userAttributes);
606606
Map<String, List<String>> filteredAttributeLists = (Map<String, List<String>>) KitConfiguration.filterAttributes(provider.getConfiguration().getUserAttributeFilters(),
607607
userAttributeLists);
608+
boolean supportsAttributeLists = ((KitIntegration.BaseAttributeListener) provider).supportsAttributeLists();
608609
if (provider instanceof KitIntegration.AttributeListener) {
609-
if (((KitIntegration.AttributeListener) provider).supportsAttributeLists()) {
610+
if (supportsAttributeLists) {
610611
((KitIntegration.AttributeListener) provider).setAllUserAttributes(filteredAttributeSingles, filteredAttributeLists);
611612
} else {
612613
Map<String, String> singlesCopy = new HashMap<>(filteredAttributeSingles);
@@ -617,7 +618,7 @@ public void onUserAttributesReceived(Map<String, String> userAttributes, Map<Str
617618
}
618619
}
619620
if (provider instanceof KitIntegration.UserAttributeListener) {
620-
if (((KitIntegration.UserAttributeListener) provider).supportsAttributeLists()) {
621+
if (supportsAttributeLists) {
621622
((KitIntegration.UserAttributeListener) provider).onSetAllUserAttributes(filteredAttributeSingles, filteredAttributeLists, FilteredMParticleUser.getInstance(mpid, provider));
622623
} else {
623624
Map<String, String> singlesCopy = new HashMap<>(filteredAttributeSingles);
@@ -683,15 +684,16 @@ private void setUserAttribute(KitIntegration provider, String attributeKey, List
683684
if ((provider instanceof KitIntegration.AttributeListener || provider instanceof KitIntegration.UserAttributeListener)
684685
&& !provider.isDisabled()
685686
&& KitConfiguration.shouldForwardAttribute(provider.getConfiguration().getUserAttributeFilters(), attributeKey)) {
687+
boolean supportsAttributeLists = ((KitIntegration.BaseAttributeListener) provider).supportsAttributeLists();
686688
if (provider instanceof KitIntegration.AttributeListener) {
687-
if (((KitIntegration.AttributeListener) provider).supportsAttributeLists()) {
689+
if (supportsAttributeLists) {
688690
((KitIntegration.AttributeListener) provider).setUserAttributeList(attributeKey, valueList);
689691
} else {
690692
((KitIntegration.AttributeListener) provider).setUserAttribute(attributeKey, KitUtils.join(valueList));
691693
}
692694
}
693695
if (provider instanceof KitIntegration.UserAttributeListener) {
694-
if (((KitIntegration.UserAttributeListener) provider).supportsAttributeLists()) {
696+
if (supportsAttributeLists) {
695697
((KitIntegration.UserAttributeListener) provider).onSetUserAttributeList(attributeKey, valueList, FilteredMParticleUser.getInstance(mpid, provider));
696698
} else {
697699
((KitIntegration.UserAttributeListener) provider).onSetUserAttribute(attributeKey, KitUtils.join(valueList), FilteredMParticleUser.getInstance(mpid, provider));
@@ -721,15 +723,10 @@ public void removeUserAttribute(String key, long mpid) {
721723
}
722724
for (KitIntegration provider : providers.values()) {
723725
try {
724-
if ((provider instanceof KitIntegration.AttributeListener || provider instanceof KitIntegration.UserAttributeListener)
726+
if ((provider instanceof KitIntegration.BaseAttributeListener listener)
725727
&& !provider.isDisabled()
726728
&& KitConfiguration.shouldForwardAttribute(provider.getConfiguration().getUserAttributeFilters(), key)) {
727-
if (provider instanceof KitIntegration.AttributeListener) {
728-
((KitIntegration.AttributeListener) provider).removeUserAttribute(key);
729-
}
730-
if (provider instanceof KitIntegration.UserAttributeListener) {
731-
((KitIntegration.UserAttributeListener) provider).onRemoveUserAttribute(key, FilteredMParticleUser.getInstance(mpid, provider));
732-
}
729+
listener.onRemoveUserAttribute(key, FilteredMParticleUser.getInstance(mpid, provider));
733730
}
734731
} catch (Exception e) {
735732
Logger.warning("Failed to call removeUserAttribute/onRemoveUserAttribute for kit: " + provider.getName() + ": " + e.getMessage());

kits/adobe/adobe-5/src/main/kotlin/com/mparticle/kits/AdobeKitBase.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ abstract class AdobeKitBase :
7171
syncIds()
7272
}
7373

74-
override fun removeUserAttribute(s: String) {
74+
override fun onRemoveUserAttribute(
75+
key: String,
76+
user: FilteredMParticleUser,
77+
) {
7578
syncIds()
7679
}
7780

@@ -93,7 +96,9 @@ abstract class AdobeKitBase :
9396
override fun onPushMessageReceived(
9497
context: Context,
9598
intent: Intent,
96-
) {}
99+
) {
100+
// No-op: this kit does not implement push message handling.
101+
}
97102

98103
override fun onPushRegistration(
99104
instanceId: String,

kits/adobemedia/adobemedia-5/src/main/kotlin/com/mparticle/kits/AdobeKit.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ open class AdobeKit :
108108
syncIds()
109109
}
110110

111-
override fun removeUserAttribute(s: String) {
111+
override fun onRemoveUserAttribute(
112+
key: String,
113+
user: FilteredMParticleUser,
114+
) {
112115
syncIds()
113116
}
114117

kits/appsflyer/appsflyer-6/src/main/kotlin/com/mparticle/kits/AppsFlyerKit.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ class AppsFlyerKit :
277277
value: Any?,
278278
user: FilteredMParticleUser?,
279279
) {
280+
// No-op: this kit does not implement this feature.
280281
}
281282

282283
override fun onSetUserTag(
@@ -304,9 +305,9 @@ class AppsFlyerKit :
304305
override fun setAllUserAttributes(
305306
map: Map<String, String>,
306307
map1: Map<String, List<String>>,
307-
) {}
308-
309-
override fun removeUserAttribute(key: String) {}
308+
) {
309+
// No-op: this kit does not implement this feature.
310+
}
310311

311312
override fun removeUserIdentity(identityType: MParticle.IdentityType) {
312313
with(instance) {

kits/apptimize/apptimize-3/src/main/kotlin/com/mparticle/kits/ApptimizeKit.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,10 @@ class ApptimizeKit :
152152
}
153153
}
154154

155-
override fun removeUserAttribute(key: String) {
155+
override fun onRemoveUserAttribute(
156+
key: String,
157+
user: FilteredMParticleUser,
158+
) {
156159
Apptimize.clearUserAttribute(key)
157160
}
158161

kits/branch/branch-5/src/main/kotlin/com/mparticle/kits/BranchMetricsKit.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,19 @@ class BranchMetricsKit :
191191
map1: Map<String, List<String>>,
192192
) {}
193193

194-
override fun removeUserAttribute(s: String) {}
194+
override fun onRemoveUserAttribute(
195+
key: String,
196+
user: FilteredMParticleUser,
197+
) {
198+
// No-op: this kit does not implement this feature.
199+
}
195200

196201
override fun setUserIdentity(
197202
identityType: IdentityType,
198203
s: String,
199-
) {}
204+
) {
205+
// No-op: this kit does not implement this feature.
206+
}
200207

201208
override fun removeUserIdentity(identityType: IdentityType) {}
202209

kits/braze/braze-38/src/main/kotlin/com/mparticle/kits/AppboyKit.kt

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,37 @@ open class AppboyKit :
444444
key: String?,
445445
user: FilteredMParticleUser?,
446446
) {
447+
if (key == null) {
448+
return
449+
}
450+
var keyMut = key
451+
Braze
452+
.getInstance(context)
453+
.getCurrentUser(
454+
object : IValueCallback<BrazeUser> {
455+
override fun onSuccess(value: BrazeUser) {
456+
when (keyMut) {
457+
UserAttributes.CITY -> value.setHomeCity(null)
458+
UserAttributes.COUNTRY -> value.setCountry(null)
459+
UserAttributes.FIRSTNAME -> value.setFirstName(null)
460+
UserAttributes.LASTNAME -> value.setLastName(null)
461+
UserAttributes.MOBILE_NUMBER -> value.setPhoneNumber(null)
462+
else -> {
463+
var customKey = keyMut
464+
if (customKey.startsWith("$")) {
465+
customKey = customKey.substring(1)
466+
}
467+
value.unsetCustomUserAttribute(customKey)
468+
}
469+
}
470+
queueDataFlush()
471+
}
472+
473+
override fun onError() {
474+
Logger.warning("unable to remove User Attribute with key: " + key)
475+
}
476+
},
477+
)
447478
}
448479

449480
override fun onSetUserAttribute(
@@ -457,13 +488,15 @@ open class AppboyKit :
457488
key: String?,
458489
user: FilteredMParticleUser?,
459490
) {
491+
// No-op: this kit does not implement this feature.
460492
}
461493

462494
override fun onSetUserAttributeList(
463495
attributeKey: String?,
464496
attributeValueList: MutableList<String>?,
465497
user: FilteredMParticleUser?,
466498
) {
499+
// No-op: this kit does not implement this feature.
467500
}
468501

469502
override fun onSetAllUserAttributes(
@@ -626,45 +659,14 @@ open class AppboyKit :
626659
}
627660
}
628661

629-
override fun removeUserAttribute(keyIn: String) {
630-
var key = keyIn
631-
Braze
632-
.getInstance(context)
633-
.getCurrentUser(
634-
object : IValueCallback<BrazeUser> {
635-
override fun onSuccess(value: BrazeUser) {
636-
if (UserAttributes.CITY == key) {
637-
value.setHomeCity(null)
638-
} else if (UserAttributes.COUNTRY == key) {
639-
value.setCountry(null)
640-
} else if (UserAttributes.FIRSTNAME == key) {
641-
value.setFirstName(null)
642-
} else if (UserAttributes.LASTNAME == key) {
643-
value.setLastName(null)
644-
} else if (UserAttributes.MOBILE_NUMBER == key) {
645-
value.setPhoneNumber(null)
646-
} else {
647-
if (key.startsWith("$")) {
648-
key = key.substring(1)
649-
}
650-
value.unsetCustomUserAttribute(key)
651-
}
652-
queueDataFlush()
653-
}
654-
655-
override fun onError() {
656-
Logger.warning("unable to remove User Attribute with key: " + key)
657-
}
658-
},
659-
)
660-
}
661-
662662
override fun setUserIdentity(
663663
identityType: IdentityType,
664664
identity: String,
665665
) {}
666666

667-
override fun removeUserIdentity(identityType: IdentityType) {}
667+
override fun removeUserIdentity(identityType: IdentityType) {
668+
// No-op: this kit does not implement this feature.
669+
}
668670

669671
override fun logout(): List<ReportingMessage> = emptyList()
670672

0 commit comments

Comments
 (0)