Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ open class AttributeListenerTestKit :
AttributeListener,
LogoutListener {
var setUserAttributeCallback: ((attributeKey: String?, attributeValue: String?) -> Unit)? = null
var setUserAttributeList: ((attributeKey: String?, attributeValueList: List<String?>?) -> Unit)? =
var setUserAttributeList: ((attributeKey: String?, attributeValueList: List<String>?) -> Unit)? =
null
var supportsAttributeLists: (() -> Boolean)? = null
var setAllUserAttributes: ((userAttributes: Map<String, String>?, userAttributeLists: Map<String, List<String>>?) -> Unit)? =
Expand All @@ -24,9 +24,10 @@ open class AttributeListenerTestKit :

override fun supportsAttributeLists() = supportsAttributeLists?.invoke() ?: true

override fun setUserAttributeList(
attributeKey: String,
attributeValueList: MutableList<String>,
override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
setUserAttributeList?.invoke(attributeKey, attributeValueList)
onAttributeReceived?.invoke(attributeKey, attributeValueList)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ open class UserAttributeListenerTestKit :
var onSetUserAttribute: ((key: String?, value: Any?, user: FilteredMParticleUser?) -> Unit)? =
null
var onSetUserTag: ((key: String?, user: FilteredMParticleUser?) -> Unit)? = null
var onSetUserAttributeList: ((attributeKey: String?, attributeValueList: List<String?>?, user: FilteredMParticleUser?) -> Unit)? =
var onSetUserAttributeList: ((attributeKey: String?, attributeValueList: List<String>?, user: FilteredMParticleUser?) -> Unit)? =
null
var onSetAllUserAttributes: (
(
Expand Down Expand Up @@ -75,7 +75,7 @@ open class UserAttributeListenerTestKit :

override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: MutableList<String>?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
onSetUserAttributeList?.invoke(attributeKey, attributeValueList, user)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,12 +396,22 @@ public interface BaseAttributeListener {
* @param user filtered user context for this kit
*/
void onSetUserAttribute(String key, Object value, FilteredMParticleUser user);

/**
* Called when a list-valued user attribute is set and {@link #supportsAttributeLists()} returns true.
*
* @param attributeKey attribute key (may be null)
* @param attributeValueList attribute values (may be null)
* @param user filtered user context for this kit (may be null)
*/
void onSetUserAttributeList(
@Nullable String attributeKey,
@Nullable List<String> attributeValueList,
@Nullable FilteredMParticleUser user);
}

public interface AttributeListener extends BaseAttributeListener {

void setUserAttributeList(String attributeKey, List<String> attributeValueList);

void setAllUserAttributes(Map<String, String> userAttributes, Map<String, List<String>> userAttributeLists);

void setUserIdentity(MParticle.IdentityType identityType, String identity);
Expand Down Expand Up @@ -565,8 +575,6 @@ public interface UserAttributeListener extends BaseAttributeListener {

void onSetUserTag(String key, FilteredMParticleUser user);

void onSetUserAttributeList(String attributeKey, List<String> attributeValueList, FilteredMParticleUser user);

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

void onConsentStateUpdated(ConsentState oldState, ConsentState newState, FilteredMParticleUser user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,24 +681,15 @@ public void setUserAttributeList(String attributeKey, List<String> valuesList, l
}

private void setUserAttribute(KitIntegration provider, String attributeKey, List<String> valueList, long mpid) {
if ((provider instanceof KitIntegration.AttributeListener || provider instanceof KitIntegration.UserAttributeListener)
if ((provider instanceof KitIntegration.BaseAttributeListener listener)
&& !provider.isDisabled()
&& KitConfiguration.shouldForwardAttribute(provider.getConfiguration().getUserAttributeFilters(), attributeKey)) {
boolean supportsAttributeLists = ((KitIntegration.BaseAttributeListener) provider).supportsAttributeLists();
boolean supportsAttributeLists = listener.supportsAttributeLists();
FilteredMParticleUser user = FilteredMParticleUser.getInstance(mpid, provider);
if (provider instanceof KitIntegration.AttributeListener) {
if (supportsAttributeLists) {
((KitIntegration.AttributeListener) provider).setUserAttributeList(attributeKey, valueList);
} else {
((KitIntegration.BaseAttributeListener) provider).onSetUserAttribute(attributeKey, KitUtils.join(valueList), user);
}
}
if (provider instanceof KitIntegration.UserAttributeListener) {
if (supportsAttributeLists) {
((KitIntegration.UserAttributeListener) provider).onSetUserAttributeList(attributeKey, valueList, user);
} else {
((KitIntegration.UserAttributeListener) provider).onSetUserAttribute(attributeKey, KitUtils.join(valueList), user);
}
if (supportsAttributeLists) {
listener.onSetUserAttributeList(attributeKey, valueList, user);
} else {
listener.onSetUserAttribute(attributeKey, KitUtils.join(valueList), user);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,8 +558,8 @@ class KitManagerImplTest {
attributeList.add("2")
attributeList.add("3")
manager.setUserAttributeList("test key", attributeList, 1)
verify(integration as AttributeListener, Mockito.times(1))
.setUserAttributeList("test key", attributeList)
verify(integration as BaseAttributeListener, Mockito.times(1))
.onSetUserAttributeList(eq("test key"), eq(attributeList), any())
verify(integration2 as BaseAttributeListener, Mockito.times(1))
.onSetUserAttribute(eq("test key"), eq("1,2,3"), isNull())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ abstract class AdobeKitBase :
syncIds()
}

override fun setUserAttributeList(
s: String,
list: List<String>,
override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
syncIds()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ open class AdobeKit :
syncIds()
}

override fun setUserAttributeList(
s: String,
list: List<String>,
override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
syncIds()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,6 @@ class AppsFlyerKit :
return messageList
}

override fun setUserAttributeList(
s: String,
list: List<String>,
) {}

override fun onIncrementUserAttribute(
key: String?,
incrementedBy: Number?,
Expand Down Expand Up @@ -283,9 +278,10 @@ class AppsFlyerKit :

override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: MutableList<String>?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
// not supported
}

override fun onSetAllUserAttributes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class ApptentiveKit :

override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: MutableList<String>?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
// Ignored
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,12 @@ class ApptimizeKit :

override fun getName(): String = KIT_NAME

/**
* Not supported by the Apptimize kit.
*/
override fun setUserAttributeList(
key: String,
list: List<String>,
override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
// not supported
// not supported by the Apptimize kit
}

override fun supportsAttributeLists(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,13 @@ class BranchMetricsKit :
)
}

override fun setUserAttributeList(
s: String,
list: List<String>,
) {}
override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
// not supported
}

override fun supportsAttributeLists(): Boolean = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@
}

override fun onError() {
Logger.warning("unable to set key: " + keyIn + " with value: " + attributeValue)

Check failure on line 319 in kits/braze/braze-38/src/main/kotlin/com/mparticle/kits/AppboyKit.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "unable to set key: " 3 times.

See more on https://sonarcloud.io/project/issues?id=mParticle_mparticle-android-sdk&issues=AZ1FTxqyoxp4O0LdenBJ&open=AZ1FTxqyoxp4O0LdenBJ&pullRequest=690
}
},
)
Expand Down Expand Up @@ -440,27 +440,6 @@
}
}

override fun setUserAttributeList(
key: String,
list: List<String>,
) {
Braze
.getInstance(context)
.getCurrentUser(
object : IValueCallback<BrazeUser> {
override fun onSuccess(value: BrazeUser) {
val array = list.toTypedArray<String?>()
value.setCustomAttributeArray(key, array)
queueDataFlush()
}

override fun onError() {
Logger.warning("unable to set key: " + key + " with User Attribute List: " + list)
}
},
)
}

override fun onIncrementUserAttribute(
key: String?,
incrementedBy: Number?,
Expand Down Expand Up @@ -526,10 +505,29 @@

override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: MutableList<String>?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
// No-op: this kit does not implement this feature.
if (attributeKey == null || attributeValueList == null) {
return
}
Braze
.getInstance(context)
.getCurrentUser(
object : IValueCallback<BrazeUser> {
override fun onSuccess(value: BrazeUser) {
val array = attributeValueList.toTypedArray<String?>()
value.setCustomAttributeArray(attributeKey, array)
queueDataFlush()
}

override fun onError() {
Logger.warning(
"unable to set key: " + attributeKey + " with User Attribute List: " + attributeValueList,
)
}
},
)
}

override fun onSetAllUserAttributes(
Expand Down Expand Up @@ -686,7 +684,21 @@
applyScalarUserAttribute(key, value)
}
for ((key, value) in attributeLists) {
setUserAttributeList(key, value)
Braze
.getInstance(context)
.getCurrentUser(
object : IValueCallback<BrazeUser> {
override fun onSuccess(brazeUser: BrazeUser) {
val array = value.toTypedArray<String?>()
brazeUser.setCustomAttributeArray(key, array)
queueDataFlush()
}

override fun onError() {
Logger.warning("unable to set key: " + key + " with User Attribute List: " + 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.

Duplicated Braze API logic instead of delegating to method

Medium Severity

setAllUserAttributes inlines the full Braze getCurrentUser callback pattern (~15 lines) instead of delegating to onSetUserAttributeList(key, value, null), which contains the identical logic. The old code avoided this by calling setUserAttributeList(key, value). Since key and value from the map are non-null, the null guard in onSetUserAttributeList will pass, and the user parameter is unused — making delegation safe and removing a significant maintenance risk across all four Braze kit versions.

Additional Locations (2)
Fix in Cursor Fix in Web

}
kitPreferences.edit().putBoolean(PREF_KEY_HAS_SYNCED_ATTRIBUTES, true).apply()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@
}

override fun onError() {
Logger.warning("unable to set key: " + keyIn + " with value: " + attributeValue)

Check failure on line 319 in kits/braze/braze-39/src/main/kotlin/com/mparticle/kits/AppboyKit.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "unable to set key: " 3 times.

See more on https://sonarcloud.io/project/issues?id=mParticle_mparticle-android-sdk&issues=AZ1FTxn7oxp4O0LdenBI&open=AZ1FTxn7oxp4O0LdenBI&pullRequest=690
}
},
)
Expand Down Expand Up @@ -440,27 +440,6 @@
}
}

override fun setUserAttributeList(
key: String,
list: List<String>,
) {
Braze
.getInstance(context)
.getCurrentUser(
object : IValueCallback<BrazeUser> {
override fun onSuccess(value: BrazeUser) {
val array = list.toTypedArray<String?>()
value.setCustomAttributeArray(key, array)
queueDataFlush()
}

override fun onError() {
Logger.warning("unable to set key: " + key + " with User Attribute List: " + list)
}
},
)
}

override fun onIncrementUserAttribute(
key: String?,
incrementedBy: Number?,
Expand Down Expand Up @@ -526,10 +505,29 @@

override fun onSetUserAttributeList(
attributeKey: String?,
attributeValueList: MutableList<String>?,
attributeValueList: List<String>?,
user: FilteredMParticleUser?,
) {
// No-op: this kit does not implement this feature.
if (attributeKey == null || attributeValueList == null) {
return
}
Braze
.getInstance(context)
.getCurrentUser(
object : IValueCallback<BrazeUser> {
override fun onSuccess(value: BrazeUser) {
val array = attributeValueList.toTypedArray<String?>()
value.setCustomAttributeArray(attributeKey, array)
queueDataFlush()
}

override fun onError() {
Logger.warning(
"unable to set key: " + attributeKey + " with User Attribute List: " + attributeValueList,
)
}
},
)
}

override fun onSetAllUserAttributes(
Expand Down Expand Up @@ -686,7 +684,21 @@
applyScalarUserAttribute(key, value)
}
for ((key, value) in attributeLists) {
setUserAttributeList(key, value)
Braze
.getInstance(context)
.getCurrentUser(
object : IValueCallback<BrazeUser> {
override fun onSuccess(brazeUser: BrazeUser) {
val array = value.toTypedArray<String?>()
brazeUser.setCustomAttributeArray(key, array)
queueDataFlush()
}

override fun onError() {
Logger.warning("unable to set key: " + key + " with User Attribute List: " + value)
}
},
)
}
kitPreferences.edit().putBoolean(PREF_KEY_HAS_SYNCED_ATTRIBUTES, true).apply()
}
Expand Down
Loading
Loading