From 7aa95284f491da1a30ef85d0d27dbbfddf305940 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Tue, 23 Dec 2025 00:18:55 +0530 Subject: [PATCH 01/23] Reevaluatoin Added to Launch Rules Engine --- .../mobile/launch/rulesengine/LaunchRule.kt | 7 +- .../launch/rulesengine/LaunchRulesEngine.java | 105 +++++++++++++++--- 2 files changed, 93 insertions(+), 19 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt index ec205275a..a02606c37 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt @@ -19,9 +19,14 @@ import com.adobe.marketing.mobile.rulesengine.Rule * * @property condition an object of [Evaluable] * @property consequenceList a list of [RuleConsequence] objects + * @property reevaluable a boolean indicating if the rule should be reevaluated * @constructor Constructs a new [LaunchRule] */ -data class LaunchRule(val condition: Evaluable, val consequenceList: List) : Rule { +data class LaunchRule @JvmOverloads constructor( + val condition: Evaluable, + val consequenceList: List, + val reevaluable: Boolean = false +) : Rule { override fun getEvaluable(): Evaluable { return condition } diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index 577282742..76fcdfc6a 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -27,6 +27,25 @@ public class LaunchRulesEngine { + /** A callback that should be invoked when the asynchronous work is complete. */ + public interface CompletionCallback { + void onComplete(); + } + + /** + * An interface for an interceptor that is triggered when a {@link LaunchRule} with the re-evaluation + * flag is triggered. The interceptor is responsible for updating the rules and invoking the {@link + * CompletionCallback} when complete. + */ + public interface RuleReevaluationInterceptor { + void onReevaluationTriggered( + final Event event, + final List revaluableRules, + final CompletionCallback callback); + } + + private RuleReevaluationInterceptor reevaluationInterceptor; + @VisibleForTesting static final String RULES_ENGINE_NAME = "name"; private final String name; private final RulesEngine ruleRulesEngine; @@ -61,6 +80,15 @@ public LaunchRulesEngine(@NonNull final String name, @NonNull final ExtensionApi this.ruleRulesEngine = ruleEngine; } + /** + * Sets the {@link RuleReevaluationInterceptor} for this {@link LaunchRulesEngine}. + * + * @param interceptor the interceptor to be set. + */ + public void setRuleReevaluationInterceptor(final RuleReevaluationInterceptor interceptor) { + this.reevaluationInterceptor = interceptor; + } + /** * Set a new set of rules, the new rules replace the current rules. * @@ -91,8 +119,8 @@ public void addRules(final List rules) { /** * Processes the supplied event with all the rules that match it. This processing may result in - * dispatch of the supplied event after attachment, modification of its data or dispatch of a - * new event from the supplied event. + * dispatch of the supplied event after attachment, modification of its data or dispatch of a new + * event from the supplied event. * * @param event the event to be evaluated * @return the processed [Event] after token replacement. @@ -102,20 +130,20 @@ public Event processEvent(@NonNull final Event event) { throw new IllegalArgumentException("Cannot evaluate null event."); } - final List matchedRules = - ruleRulesEngine.evaluate(new LaunchTokenFinder(event, extensionApi)); - - // if initial rule set has not been received, cache the event to be processed - // when rules are set if (!initialRulesReceived) { handleCaching(event); + // If the event was cached, don't process it now. It will be processed later. + // If it was a reset event, it triggered reprocessing of other events. Don't process it. + return event; } - return launchRulesConsequence.process(event, matchedRules); + + return processAndIntercept(event); } /** * Evaluates the supplied event against the all current rules and returns the {@link - * RuleConsequence}'s from the rules that matched the supplied event. + * RuleConsequence}'s from the rules that matched the supplied event. This method is synchronous + * and does not trigger any re-evaluation interceptors. * * @param event the event to be evaluated * @return a {@code List} that match the supplied event. @@ -125,13 +153,54 @@ public List evaluateEvent(@NonNull final Event event) { throw new IllegalArgumentException("Cannot evaluate null event."); } - final List matchedRules = - ruleRulesEngine.evaluate(new LaunchTokenFinder(event, extensionApi)); + final LaunchTokenFinder tokenFinder = new LaunchTokenFinder(event, extensionApi); + final List matchedRules = ruleRulesEngine.evaluate(tokenFinder); // get token replaced consequences return launchRulesConsequence.evaluate(event, matchedRules); } + private Event processAndIntercept(final Event event) { + final LaunchTokenFinder tokenFinder = new LaunchTokenFinder(event, extensionApi); + final List matchedRules = ruleRulesEngine.evaluate(tokenFinder); + + // If no interceptor is set, process consequences immediately. + if (reevaluationInterceptor == null) { + return launchRulesConsequence.process(event, matchedRules); + } + + final List revaluableRules = getRevaluableRules(matchedRules); + + // If there are revaluable rules, defer processing until the interceptor completes. + if (!revaluableRules.isEmpty()) { + reevaluationInterceptor.onReevaluationTriggered( + event, + revaluableRules, + () -> { + // After the interceptor has updated the rules, re-evaluate and process + // consequences + final List newlyMatchedRules = + ruleRulesEngine.evaluate(tokenFinder); + launchRulesConsequence.process(event, newlyMatchedRules); + }); + // Return the original event; consequences will be processed asynchronously. + return event; + } else { + // No re-evaluation needed, so process the consequences immediately. + return launchRulesConsequence.process(event, matchedRules); + } + } + + private List getRevaluableRules(final List rules) { + final List revaluableRules = new ArrayList<>(); + for (final LaunchRule rule : rules) { + if (rule.getReevaluable()) { + revaluableRules.add(rule); + } + } + return revaluableRules; + } + List getRules() { return ruleRulesEngine.getRules(); } @@ -142,14 +211,14 @@ int getCachedEventCount() { } private void reprocessCachedEvents() { - for (Event cachedEvent : cachedEvents) { - final List matchedRules = - ruleRulesEngine.evaluate(new LaunchTokenFinder(cachedEvent, extensionApi)); - launchRulesConsequence.process(cachedEvent, matchedRules); - } - // clear cached events and set the flag to indicate that rules were set at least once + // To avoid ConcurrentModificationException, we process a copy of the cached events. + final List eventsToReprocess = new ArrayList<>(cachedEvents); cachedEvents.clear(); - initialRulesReceived = true; + initialRulesReceived = true; // Set before processing to prevent re-caching + + for (final Event cachedEvent : eventsToReprocess) { + processEvent(cachedEvent); + } } private void handleCaching(final Event event) { From f3b0112e6fa3a882e4737f8b26b76ee4ecf5a181 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Fri, 2 Jan 2026 16:31:37 +0530 Subject: [PATCH 02/23] Updated Reevaluation logic and added test cases for backward compatibility of LaunchRule object creation --- .../launch/rulesengine/LaunchRulesEngine.java | 57 +++++++++++-------- .../rulesengine/LaunchRuleJavaTests.java | 38 +++++++++++++ .../launch/rulesengine/LaunchRuleTests.kt | 35 ++++++++++++ 3 files changed, 107 insertions(+), 23 deletions(-) create mode 100644 code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java create mode 100644 code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index 76fcdfc6a..25cfb566e 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -130,11 +130,10 @@ public Event processEvent(@NonNull final Event event) { throw new IllegalArgumentException("Cannot evaluate null event."); } + // if initial rule set has not been received, cache the event to be processed + // when rules are set if (!initialRulesReceived) { handleCaching(event); - // If the event was cached, don't process it now. It will be processed later. - // If it was a reset event, it triggered reprocessing of other events. Don't process it. - return event; } return processAndIntercept(event); @@ -162,32 +161,44 @@ public List evaluateEvent(@NonNull final Event event) { private Event processAndIntercept(final Event event) { final LaunchTokenFinder tokenFinder = new LaunchTokenFinder(event, extensionApi); - final List matchedRules = ruleRulesEngine.evaluate(tokenFinder); + final ArrayList matchedRules = new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); + + //If there are no matching events, nothing to do further + if(matchedRules.isEmpty()) { + return event; + } // If no interceptor is set, process consequences immediately. if (reevaluationInterceptor == null) { return launchRulesConsequence.process(event, matchedRules); - } - - final List revaluableRules = getRevaluableRules(matchedRules); - - // If there are revaluable rules, defer processing until the interceptor completes. - if (!revaluableRules.isEmpty()) { - reevaluationInterceptor.onReevaluationTriggered( - event, - revaluableRules, - () -> { - // After the interceptor has updated the rules, re-evaluate and process - // consequences - final List newlyMatchedRules = - ruleRulesEngine.evaluate(tokenFinder); - launchRulesConsequence.process(event, newlyMatchedRules); - }); + } else { + final ArrayList revaluableRules = new ArrayList<>(getRevaluableRules(matchedRules)); + final List nonRevaluableRules = new ArrayList<>(matchedRules); + nonRevaluableRules.removeAll(revaluableRules); + + // If there are revaluable rules, defer processing until the interceptor completes. + if (!revaluableRules.isEmpty()) { + //trigger reevaluation for revaluable rules + reevaluationInterceptor.onReevaluationTriggered( + event, + revaluableRules, + () -> { + // After the interceptor has updated the rules, re-evaluate and process + // consequences + final List newlyMatchedRules = + ruleRulesEngine.evaluate(tokenFinder); + for (final LaunchRule triggeredRule : nonRevaluableRules) { + newlyMatchedRules.remove(triggeredRule); + } + launchRulesConsequence.process(event, newlyMatchedRules); + }); + } + if(!nonRevaluableRules.isEmpty()) { + // No re-evaluation needed, so process the consequences immediately. + return launchRulesConsequence.process(event, nonRevaluableRules); + } // Return the original event; consequences will be processed asynchronously. return event; - } else { - // No re-evaluation needed, so process the consequences immediately. - return launchRulesConsequence.process(event, matchedRules); } } diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java new file mode 100644 index 000000000..cca5bcfda --- /dev/null +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java @@ -0,0 +1,38 @@ +package com.adobe.marketing.mobile.launch.rulesengine; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import com.adobe.marketing.mobile.rulesengine.Evaluable; + +import org.junit.Test; + +import java.util.Collections; + +public class LaunchRuleJavaTests { + + private final Evaluable mockEvaluable = mock(Evaluable.class); + private final RuleConsequence mockConsequence = mock(RuleConsequence.class); + + @Test + public void testLaunchRuleCreationWithoutRevaluable() { + final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); + assertFalse(rule.getReevaluable()); + assertEquals(mockEvaluable, rule.getEvaluable()); + } + + @Test + public void testLaunchRuleCreationWithRevaluableFalse() { + final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence), false); + assertFalse(rule.getReevaluable()); + assertEquals(mockEvaluable, rule.getEvaluable()); + } + + @Test + public void testLaunchRuleCreationWithRevaluableTrue() { + final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence), true); + assertTrue(rule.getReevaluable()); + assertEquals(mockEvaluable, rule.getEvaluable()); + } +} \ No newline at end of file diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt new file mode 100644 index 000000000..1ea697052 --- /dev/null +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt @@ -0,0 +1,35 @@ +package com.adobe.marketing.mobile.launch.rulesengine + +import com.adobe.marketing.mobile.rulesengine.Evaluable +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.mockito.kotlin.mock + +class LaunchRuleTests { + + private val mockEvaluable: Evaluable = mock() + private val mockConsequence: RuleConsequence = mock() + + @Test + fun `test LaunchRule creation without revaluable parameter`() { + val rule = LaunchRule(mockEvaluable, listOf(mockConsequence)) + assertFalse(rule.reevaluable) + assertEquals(mockEvaluable, rule.evaluable) + } + + @Test + fun `test LaunchRule creation with revaluable as false`() { + val rule = LaunchRule(mockEvaluable, listOf(mockConsequence), false) + assertFalse(rule.reevaluable) + assertEquals(mockEvaluable, rule.evaluable) + } + + @Test + fun `test LaunchRule creation with revaluable as true`() { + val rule = LaunchRule(mockEvaluable, listOf(mockConsequence), true) + assertTrue(rule.reevaluable) + assertEquals(mockEvaluable, rule.evaluable) + } +} \ No newline at end of file From 5149dd9a680a64aad1ae1e0bad4a476024bbd6ca Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Fri, 2 Jan 2026 20:45:35 +0530 Subject: [PATCH 03/23] Adding Reevaluable key parsing logic to JSONRule and JSONRuleRoot --- .../marketing/mobile/launch/rulesengine/json/JSONRule.kt | 4 ++-- .../mobile/launch/rulesengine/json/JSONRuleRoot.kt | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRule.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRule.kt index d50aabde3..fe45b9b06 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRule.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRule.kt @@ -61,7 +61,7 @@ internal class JSONRule private constructor( * @return an object of [LaunchRule] */ @JvmSynthetic - internal fun toLaunchRule(extensionApi: ExtensionApi): LaunchRule? { + internal fun toLaunchRule(extensionApi: ExtensionApi, reEvaluable: Boolean): LaunchRule? { val evaluable = JSONCondition.build(condition, extensionApi)?.toEvaluable() if (evaluable !is Evaluable) { Log.error( @@ -74,6 +74,6 @@ internal class JSONRule private constructor( val consequenceList = consequences.map { JSONConsequence(it as? JSONObject)?.toRuleConsequence() ?: throw Exception() } - return LaunchRule(evaluable, consequenceList) + return LaunchRule(evaluable, consequenceList, reEvaluable) } } diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleRoot.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleRoot.kt index 565373c02..8e3b19d40 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleRoot.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleRoot.kt @@ -22,14 +22,16 @@ import org.json.JSONObject /** * The class representing a set of Rules */ -internal class JSONRuleRoot private constructor(val version: String, val jsonArray: JSONArray) { +internal class JSONRuleRoot private constructor(val version: String, val jsonArray: JSONArray, val reEvaluable: Boolean) { companion object { private const val LOG_TAG = "JSONRuleRoot" private const val KEY_VERSION = "version" private const val KEY_RULES = "rules" + private const val KEY_REEVALUABLE = "reEvaluable" operator fun invoke(jsonObject: JSONObject): JSONRuleRoot? { val version = jsonObject.optString(KEY_VERSION, "0") val rules = jsonObject.optJSONArray(KEY_RULES) + val reeValuable = jsonObject.optBoolean(KEY_REEVALUABLE, false) if (rules !is JSONArray) { Log.error( LaunchRulesEngineConstants.LOG_TAG, @@ -38,7 +40,7 @@ internal class JSONRuleRoot private constructor(val version: String, val jsonArr ) return null } - return JSONRuleRoot(version, rules) + return JSONRuleRoot(version, rules, reeValuable) } } @@ -50,7 +52,7 @@ internal class JSONRuleRoot private constructor(val version: String, val jsonArr @JvmSynthetic fun toLaunchRules(extensionApi: ExtensionApi): List { return jsonArray.map { - JSONRule(it as? JSONObject)?.toLaunchRule(extensionApi) ?: throw Exception() + JSONRule(it as? JSONObject)?.toLaunchRule(extensionApi, reEvaluable) ?: throw Exception() } } } From d30ff813c61760eadeab12fff0c8254f7ab2321d Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Wed, 7 Jan 2026 14:33:34 +0530 Subject: [PATCH 04/23] Reevaluation based on consequence type added --- .../rulesengine/LaunchRulesConsequence.kt | 34 ++++++++++++++- .../launch/rulesengine/LaunchRulesEngine.java | 42 ++++++------------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt index 07a383b84..130352af9 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt @@ -26,6 +26,7 @@ import com.adobe.marketing.mobile.internal.util.fnv1a32 import com.adobe.marketing.mobile.internal.util.prettify import com.adobe.marketing.mobile.internal.util.toEventHistoryRequest import com.adobe.marketing.mobile.rulesengine.DelimiterPair +import com.adobe.marketing.mobile.rulesengine.Rule import com.adobe.marketing.mobile.rulesengine.Template import com.adobe.marketing.mobile.rulesengine.TokenFinder import com.adobe.marketing.mobile.services.Log @@ -69,6 +70,7 @@ internal class LaunchRulesConsequence( private const val CONSEQUENCE_EVENT_HISTORY_OPERATION_INSERT_IF_NOT_EXISTS = "insertIfNotExists" private const val ASYNC_TIMEOUT = 1000L + private val REEVALUABLE_CONSEQUENCE_TYPES = setOf(CONSEQUENCE_TYPE_SCHEMA) } /** @@ -170,6 +172,36 @@ internal class LaunchRulesConsequence( return processedConsequences } + fun getReevaluableRules(rules: MutableList): MutableList { + val revaluableRules: MutableList = java.util.ArrayList() + for (rule in rules) { + if (rule.reevaluable && rule.hasReevaluableSupportedConsequence) { + revaluableRules.add(rule) + } + } + return revaluableRules + } + + fun getRulesToHoldForReevaluation(rules: MutableList): MutableList { + val rulesToHold: MutableList = ArrayList() + for (rule in rules) { + if(rule.hasReevaluableSupportedConsequence){ + rulesToHold.add(rule) + } + } + return rulesToHold + } + + private val LaunchRule.hasReevaluableSupportedConsequence: Boolean + get() { + for (consequence in this.consequenceList) { + if (REEVALUABLE_CONSEQUENCE_TYPES.contains(consequence.type)) { + return true + } + } + return false + } + /** * Replace tokens inside the provided [RuleConsequence] with the right value * @@ -586,4 +618,4 @@ private val RuleConsequence.schema: String? get() = detail["schema"] as? String private val RuleConsequence.detailData: Map? - get() = DataReader.optTypedMap(Any::class.java, detail, "data", null) + get() = DataReader.optTypedMap(Any::class.java, detail, "data", null) \ No newline at end of file diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index 25cfb566e..d8585a80e 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -161,10 +161,10 @@ public List evaluateEvent(@NonNull final Event event) { private Event processAndIntercept(final Event event) { final LaunchTokenFinder tokenFinder = new LaunchTokenFinder(event, extensionApi); - final ArrayList matchedRules = new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); + final List matchedRules = ruleRulesEngine.evaluate(tokenFinder); //If there are no matching events, nothing to do further - if(matchedRules.isEmpty()) { + if (matchedRules.isEmpty()) { return event; } @@ -172,44 +172,28 @@ private Event processAndIntercept(final Event event) { if (reevaluationInterceptor == null) { return launchRulesConsequence.process(event, matchedRules); } else { - final ArrayList revaluableRules = new ArrayList<>(getRevaluableRules(matchedRules)); - final List nonRevaluableRules = new ArrayList<>(matchedRules); - nonRevaluableRules.removeAll(revaluableRules); + final List revaluableRules = launchRulesConsequence.getReevaluableRules(matchedRules); + if (revaluableRules.isEmpty()) { + return launchRulesConsequence.process(event, matchedRules); + } else { + final List rulesToHold = launchRulesConsequence.getRulesToHoldForReevaluation(matchedRules); + final ArrayList rulesToProcess = new ArrayList<>(matchedRules); + rulesToProcess.removeAll(rulesToHold); - // If there are revaluable rules, defer processing until the interceptor completes. - if (!revaluableRules.isEmpty()) { - //trigger reevaluation for revaluable rules reevaluationInterceptor.onReevaluationTriggered( event, revaluableRules, () -> { // After the interceptor has updated the rules, re-evaluate and process // consequences - final List newlyMatchedRules = - ruleRulesEngine.evaluate(tokenFinder); - for (final LaunchRule triggeredRule : nonRevaluableRules) { - newlyMatchedRules.remove(triggeredRule); - } + final ArrayList newlyMatchedRules = + new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); + newlyMatchedRules.removeAll(rulesToProcess); launchRulesConsequence.process(event, newlyMatchedRules); }); - } - if(!nonRevaluableRules.isEmpty()) { - // No re-evaluation needed, so process the consequences immediately. - return launchRulesConsequence.process(event, nonRevaluableRules); - } - // Return the original event; consequences will be processed asynchronously. - return event; - } - } - - private List getRevaluableRules(final List rules) { - final List revaluableRules = new ArrayList<>(); - for (final LaunchRule rule : rules) { - if (rule.getReevaluable()) { - revaluableRules.add(rule); + return launchRulesConsequence.process(event, rulesToProcess); } } - return revaluableRules; } List getRules() { From 4956617af56dea1371f4ce69153d012865197c55 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Wed, 14 Jan 2026 20:16:58 +0530 Subject: [PATCH 05/23] Launch Rules Engine Unit Tes cases fix --- .../mobile/launch/rulesengine/LaunchRulesEngine.java | 5 ----- .../mobile/launch/rulesengine/json/JSONRuleTests.kt | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index d8585a80e..b37b9243c 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -163,11 +163,6 @@ private Event processAndIntercept(final Event event) { final LaunchTokenFinder tokenFinder = new LaunchTokenFinder(event, extensionApi); final List matchedRules = ruleRulesEngine.evaluate(tokenFinder); - //If there are no matching events, nothing to do further - if (matchedRules.isEmpty()) { - return event; - } - // If no interceptor is set, process consequences immediately. if (reevaluationInterceptor == null) { return launchRulesConsequence.process(event, matchedRules); diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt index 65867219a..44391e5fc 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt @@ -24,6 +24,7 @@ import org.mockito.junit.MockitoJUnitRunner import kotlin.test.assertEquals import kotlin.test.assertNull import kotlin.test.assertTrue +import kotlin.test.assertFalse @RunWith(MockitoJUnitRunner.Silent::class) class JSONRuleTests { @@ -63,11 +64,12 @@ class JSONRuleTests { """.trimIndent() val jsonRule = JSONRule(buildJSONObject(jsonString)) assertTrue(jsonRule is JSONRule) - val launchRule = jsonRule.toLaunchRule(extensionApi) + val launchRule = jsonRule.toLaunchRule(extensionApi, false) assertTrue(launchRule is LaunchRule) assertEquals(1, launchRule.consequenceList.size) assertEquals("pb", launchRule.consequenceList[0].type) assertTrue(launchRule.condition is ComparisonExpression<*, *>) + assertFalse(launchRule.reevaluable) } @Test From d4866bf2fba2415359707cebd01b78a11db867bd Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Wed, 21 Jan 2026 09:49:54 +0530 Subject: [PATCH 06/23] Using processed event later for reevaluation processing as well --- .../mobile/launch/rulesengine/LaunchRulesEngine.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index b37b9243c..baed8397a 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -174,9 +174,9 @@ private Event processAndIntercept(final Event event) { final List rulesToHold = launchRulesConsequence.getRulesToHoldForReevaluation(matchedRules); final ArrayList rulesToProcess = new ArrayList<>(matchedRules); rulesToProcess.removeAll(rulesToHold); - + Event processedEvent = launchRulesConsequence.process(event, rulesToProcess); reevaluationInterceptor.onReevaluationTriggered( - event, + processedEvent, revaluableRules, () -> { // After the interceptor has updated the rules, re-evaluate and process @@ -184,9 +184,9 @@ private Event processAndIntercept(final Event event) { final ArrayList newlyMatchedRules = new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); newlyMatchedRules.removeAll(rulesToProcess); - launchRulesConsequence.process(event, newlyMatchedRules); + launchRulesConsequence.process(processedEvent, newlyMatchedRules); }); - return launchRulesConsequence.process(event, rulesToProcess); + return processedEvent; } } } From 1eb3ec9cca8bb1bb55d7a452cf4ec0900a0e48c6 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Wed, 21 Jan 2026 09:50:30 +0530 Subject: [PATCH 07/23] Unit test cases for Reevaluation flow --- .../LaunchRulesEngineReevaluationTests.kt | 689 ++++++++++++++++++ ...les_testNonReevaluable_addConsequence.json | 39 + ..._testNonReevaluable_schemaConsequence.json | 39 + ...les_testReevaluable_eventModification.json | 71 ++ .../rules_testReevaluable_mixedRules.json | 105 +++ ...s_testReevaluable_multipleSchemaRules.json | 72 ++ ..._testReevaluable_nonSchemaConsequence.json | 37 + ...les_testReevaluable_schemaConsequence.json | 50 ++ ...evaluable_singleRuleMixedConsequences.json | 50 ++ 9 files changed, 1152 insertions(+) create mode 100644 code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt create mode 100644 code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json create mode 100644 code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json create mode 100644 code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json create mode 100644 code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json create mode 100644 code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json create mode 100644 code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json create mode 100644 code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json create mode 100644 code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt new file mode 100644 index 000000000..c38badc08 --- /dev/null +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt @@ -0,0 +1,689 @@ +/* + Copyright 2022 Adobe. All rights reserved. + This file is licensed to you under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. You may obtain a copy + of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under + the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + OF ANY KIND, either express or implied. See the License for the specific language + governing permissions and limitations under the License. +*/ + +package com.adobe.marketing.mobile.launch.rulesengine + +import com.adobe.marketing.mobile.Event +import com.adobe.marketing.mobile.EventSource +import com.adobe.marketing.mobile.EventType +import com.adobe.marketing.mobile.ExtensionApi +import com.adobe.marketing.mobile.launch.rulesengine.json.JSONRulesParser +import com.adobe.marketing.mobile.test.util.readTestResources +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito +import org.mockito.Mockito.mock +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.KArgumentCaptor +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +/** + * Unit tests for LaunchRulesEngine reevaluation interceptor functionality. + * + * These tests cover the reevaluation feature which allows rules with schema consequences + * to be held back while an interceptor fetches updated rules before processing. + */ +@RunWith(MockitoJUnitRunner.Silent::class) +class LaunchRulesEngineReevaluationTests { + private lateinit var extensionApi: ExtensionApi + private lateinit var launchRulesEngine: LaunchRulesEngine + + @Before + fun setup() { + extensionApi = mock(ExtensionApi::class.java) + launchRulesEngine = LaunchRulesEngine("TestLaunchRulesEngine", extensionApi) + } + + // ======================================== + // Category 1: Basic Interceptor Triggering + // ======================================== + + @Test + fun `Test reevaluation interceptor is triggered when reevaluable schema rule matches`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + val eventCaptor: KArgumentCaptor = argumentCaptor() + val rulesCaptor: KArgumentCaptor> = argumentCaptor() + val callbackCaptor: KArgumentCaptor = argumentCaptor() + + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + eventCaptor.capture(), + rulesCaptor.capture(), + callbackCaptor.capture() + ) + + assertEquals(testEvent.uniqueIdentifier, eventCaptor.firstValue.uniqueIdentifier) + assertEquals(1, rulesCaptor.firstValue.size) + assertTrue(rulesCaptor.firstValue[0].reevaluable) + assertEquals("schema", rulesCaptor.firstValue[0].consequenceList[0].type) + } + + @Test + fun `Test reevaluation interceptor is NOT triggered when reevaluable is false`() { + val json = readTestResources("rules_module_tests/rules_testNonReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + // Interceptor should NOT be called for non-reevaluable rules + Mockito.verifyNoInteractions(mockInterceptor) + } + + @Test + fun `Test reevaluation interceptor is NOT triggered when no schema consequences exist`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + // Interceptor should NOT be called when there are no schema consequences + Mockito.verifyNoInteractions(mockInterceptor) + } + + @Test + fun `Test reevaluation interceptor is NOT triggered when rule does not match`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + // Event with different type that won't match the rule + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.lifecycle", + "com.adobe.eventSource.requestContent" + ).build() + + launchRulesEngine.processEvent(testEvent) + + // Interceptor should NOT be called when rules don't match + Mockito.verifyNoInteractions(mockInterceptor) + } + + @Test + fun `Test reevaluation interceptor is NOT triggered when interceptor not set`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + // Don't set interceptor + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + val eventCaptor: KArgumentCaptor = argumentCaptor() + launchRulesEngine.processEvent(testEvent) + + // Should dispatch consequence event normally + verify(extensionApi, Mockito.atLeastOnce()).dispatch(eventCaptor.capture()) + + // Verify a consequence event was dispatched + val dispatchedEvents = eventCaptor.allValues + val consequenceEvents = dispatchedEvents.filter { + it.type == EventType.RULES_ENGINE && it.source == EventSource.RESPONSE_CONTENT + } + assertTrue(consequenceEvents.isNotEmpty()) + } + + // ======================================== + // Category 2: Rule Holding and Separation + // ======================================== + + @Test + fun `Test all schema consequence rules are held for reevaluation`() { + // This test verifies that when we have SEPARATE RULES (not mixed consequences in one rule): + // 1. Reevaluable rules with schema consequences -> held + // 2. Reevaluable rules with add consequences -> processed immediately (add is not a reevaluable-supported type) + // 3. Non-reevaluable rules with add consequences -> processed immediately + + val reEvaluableRuleFile = readTestResources("rules_module_tests/rules_testReevaluable_mixedRules.json") + assertNotNull(reEvaluableRuleFile) + val reEvaluableRules = JSONRulesParser.parse(reEvaluableRuleFile, extensionApi) + // This file has 3 rules (all with reEvaluable=true): + // - Rule 1: schema consequence (will be held) + // - Rule 2: add consequence (will process immediately - add is not a reevaluable-supported type) + // - Rule 3: schema consequence (will be held) + + // Load an additional NON-reevaluable add rule + val addRuleFile = readTestResources("rules_module_tests/rules_testNonReevaluable_addConsequence.json") + assertNotNull(addRuleFile) + val addRule = JSONRulesParser.parse(addRuleFile, extensionApi) + assertNotNull(addRule) + + launchRulesEngine.replaceRules(reEvaluableRules) + launchRulesEngine.addRules(addRule) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + val eventCaptor: KArgumentCaptor = argumentCaptor() + val processedEvent = launchRulesEngine.processEvent(testEvent) + + // Verify interceptor was called + val rulesCaptor: KArgumentCaptor> = argumentCaptor() + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + eventCaptor.capture(), + rulesCaptor.capture(), + any() + ) + + // Only the 2 reevaluable schema rules should be passed to interceptor + // The add rules (both reevaluable and non-reevaluable) should NOT be in the list + assertEquals(2, rulesCaptor.firstValue.size) + assertTrue(rulesCaptor.firstValue[0].reevaluable) + assertTrue(rulesCaptor.firstValue[1].reevaluable) + + // Verify that BOTH add rule consequences were processed immediately + // (Even the reevaluable one, because add is not a reevaluable-supported consequence type) + assertNotNull(processedEvent.eventData) + // Check that initial data is preserved + assertEquals("initialValue", processedEvent.eventData?.get("initialKey")) + // Check that attached data was added + val attachedData = processedEvent.eventData?.get("attached_data") as? Map + assertEquals("addedValue", attachedData?.get("addedKey")) + } + + @Test + fun `Test single rule with mixed consequences - all consequences held together`() { + // This test verifies that if a SINGLE RULE has BOTH schema and non-schema consequences, + // the ENTIRE RULE is held (including the non-schema consequences) + + val json = readTestResources("rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + val processedEvent = launchRulesEngine.processEvent(testEvent) + + // Verify interceptor was called + val rulesCaptor: KArgumentCaptor> = argumentCaptor() + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + any(), + rulesCaptor.capture(), + any() + ) + + // The single reevaluable rule should be passed to interceptor + assertEquals(1, rulesCaptor.firstValue.size) + assertTrue(rulesCaptor.firstValue[0].reevaluable) + // Verify the rule has both consequences + assertEquals(2, rulesCaptor.firstValue[0].consequenceList.size) + + // CRITICAL: The add consequence should NOT have been processed immediately + // because the entire rule is held due to having a schema consequence + assertNotNull(processedEvent.eventData) + // Check that initial data is preserved + assertEquals("initialValue", processedEvent.eventData?.get("initialKey")) + // The attached_data should NOT be present because the rule is held + val attachedData = processedEvent.eventData?.get("attached_data") as? Map + assertEquals(null, attachedData?.get("mixedRuleKey")) + } + + @Test + fun `Test non-schema rules processed immediately while schema rules held`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_eventModification.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + val processedEvent = launchRulesEngine.processEvent(testEvent) + + // Verify the event was modified by the add consequence (processed immediately) + assertNotNull(processedEvent.eventData) + assertEquals("modifiedValue", processedEvent.eventData?.get("modifiedKey")) + assertEquals("12345", processedEvent.eventData?.get("timestamp")) + + // Verify interceptor was called (schema consequence held) + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + } + + @Test + fun `Test multiple reevaluable schema rules trigger single interceptor call`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_multipleSchemaRules.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + val rulesCaptor: KArgumentCaptor> = argumentCaptor() + + // Interceptor should be called exactly once + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + any(), + rulesCaptor.capture(), + any() + ) + + // Both reevaluable rules should be passed + assertEquals(2, rulesCaptor.firstValue.size) + assertTrue(rulesCaptor.firstValue.all { it.reevaluable }) + } + + // ======================================== + // Category 3: Callback and Re-evaluation + // ======================================== + + @Test + fun `Test callback completion processes held rules`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + val eventCaptor: KArgumentCaptor = argumentCaptor() + + launchRulesEngine.processEvent(testEvent) + + val callbackCaptor: KArgumentCaptor = argumentCaptor() + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + any(), + any(), + callbackCaptor.capture() + ) + + // Invoke the callback to simulate completion + callbackCaptor.firstValue.onComplete() + + // Verify that consequence event was dispatched after callback + verify(extensionApi, Mockito.atLeastOnce()).dispatch(eventCaptor.capture()) + + val dispatchedEvents = eventCaptor.allValues + val consequenceEvents = dispatchedEvents.filter { + it.type == EventType.RULES_ENGINE && it.source == EventSource.RESPONSE_CONTENT + } + assertTrue(consequenceEvents.isNotEmpty()) + } + + @Test + fun `Test callback re-evaluates event against current rules`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + val callbackCaptor: KArgumentCaptor = argumentCaptor() + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + any(), + any(), + callbackCaptor.capture() + ) + + // Simulate adding new rules before callback + val newJson = readTestResources("rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json") + assertNotNull(newJson) + val newRules = JSONRulesParser.parse(newJson, extensionApi) + assertNotNull(newRules) + launchRulesEngine.addRules(newRules) + + // Invoke callback - should re-evaluate with new rules + callbackCaptor.firstValue.onComplete() + + // The new rule (add consequence) should have been evaluated and event modified + // We can't directly test this without more complex mocking, but we verified callback executes + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + } + + @Test + fun `Test event passed to interceptor is correct`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event-unique", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData( + mapOf( + "customKey" to "customValue", + "userId" to "12345" + ) + ).build() + + launchRulesEngine.processEvent(testEvent) + + val eventCaptor: KArgumentCaptor = argumentCaptor() + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + eventCaptor.capture(), + any(), + any() + ) + + // Verify the exact event was passed + assertEquals(testEvent.uniqueIdentifier, eventCaptor.firstValue.uniqueIdentifier) + assertEquals("test-event-unique", eventCaptor.firstValue.name) + assertEquals("customValue", eventCaptor.firstValue.eventData?.get("customKey")) + assertEquals("12345", eventCaptor.firstValue.eventData?.get("userId")) + } + + // ======================================== + // Category 4: evaluateEvent() bypasses reevaluation + // ======================================== + + @Test + fun `Test evaluateEvent does NOT trigger reevaluation interceptor`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + // Use evaluateEvent instead of processEvent + val consequences = launchRulesEngine.evaluateEvent(testEvent) + + // Interceptor should NOT be called + Mockito.verifyNoInteractions(mockInterceptor) + + // Consequences should be returned synchronously + assertEquals(1, consequences.size) + assertEquals("schema", consequences[0].type) + } + + // ======================================== + // Category 5: Edge Cases + // ======================================== + + @Test + fun `Test reevaluation with event modification from immediate rules`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_eventModification.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData( + mapOf("originalKey" to "originalValue") + ).build() + + val processedEvent = launchRulesEngine.processEvent(testEvent) + + // Verify immediate modification happened + assertNotNull(processedEvent.eventData) + assertEquals("originalValue", processedEvent.eventData?.get("originalKey")) + assertEquals("modifiedValue", processedEvent.eventData?.get("modifiedKey")) + assertEquals("12345", processedEvent.eventData?.get("timestamp")) + + // Verify interceptor was called + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + } + + @Test + fun `Test reevaluation rules list contains only reevaluable rules`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_mixedRules.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + val rulesCaptor: KArgumentCaptor> = argumentCaptor() + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + any(), + rulesCaptor.capture(), + any() + ) + + val reevaluableRules = rulesCaptor.firstValue + // Only the reevaluable schema rule should be in the list + assertEquals(2, reevaluableRules.size) + assertTrue(reevaluableRules[0].reevaluable) + assertTrue(reevaluableRules[0].consequenceList.any { it.type == "schema" }) + } + + @Test + fun `Test reevaluation with callback invoked multiple times has no effect`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + val callbackCaptor: KArgumentCaptor = argumentCaptor() + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( + any(), + any(), + callbackCaptor.capture() + ) + + val callback = callbackCaptor.firstValue + + // Invoke callback multiple times + callback.onComplete() + callback.onComplete() + callback.onComplete() + + // Should not cause issues - just processes rules multiple times + // This is implementation-defined behavior + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + } + + @Test + fun `Test reevaluation interceptor can be updated`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor1 = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor1) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + // First interceptor should be called + verify(mockInterceptor1, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + + // Update interceptor + val mockInterceptor2 = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor2) + + launchRulesEngine.processEvent(testEvent) + + // Second interceptor should be called + verify(mockInterceptor2, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + + // First interceptor should not be called again + verify(mockInterceptor1, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + } + + @Test + fun `Test reevaluation interceptor can be cleared`() { + val json = readTestResources("rules_module_tests/rules_testReevaluable_schemaConsequence.json") + assertNotNull(json) + val rules = JSONRulesParser.parse(json, extensionApi) + assertNotNull(rules) + launchRulesEngine.replaceRules(rules) + + val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) + + val testEvent = Event.Builder( + "test-event", + "com.adobe.eventType.generic", + "com.adobe.eventSource.requestContent" + ).setEventData(mapOf("initialKey" to "initialValue")).build() + + launchRulesEngine.processEvent(testEvent) + + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + + // Clear interceptor by setting to null + launchRulesEngine.setRuleReevaluationInterceptor(null) + + val eventCaptor: KArgumentCaptor = argumentCaptor() + launchRulesEngine.processEvent(testEvent) + + // Should not call interceptor again, but should dispatch consequence normally + verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) + verify(extensionApi, Mockito.atLeastOnce()).dispatch(eventCaptor.capture()) + } +} diff --git a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json new file mode 100644 index 000000000..07df65959 --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "reEvaluable": false, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "non-reevaluable-add-1", + "type": "add", + "detail": { + "eventdata": { + "attached_data": { + "addedKey": "addedValue" + } + } + } + } + ] + } + ] +} diff --git a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json new file mode 100644 index 000000000..6dce287e8 --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "reEvaluable": false, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "non-reevaluable-schema-1", + "type": "schema", + "detail": { + "id": "test-schema-id", + "schema": "https://ns.adobe.com/personalization/message", + "data": { + "content": "test-content" + } + } + } + ] + } + ] +} diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json new file mode 100644 index 000000000..b03acba08 --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json @@ -0,0 +1,71 @@ +{ + "version": 1, + "reEvaluable": true, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "modify-event-add", + "type": "add", + "detail": { + "eventdata": { + "modifiedKey": "modifiedValue", + "timestamp": "12345" + } + } + } + ] + }, + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "held-schema-consequence", + "type": "schema", + "detail": { + "id": "test-schema-id", + "schema": "https://ns.adobe.com/personalization/message", + "data": { + "content": "schema-content" + } + } + } + ] + } + ] +} diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json new file mode 100644 index 000000000..dac7f0b6d --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json @@ -0,0 +1,105 @@ +{ + "version": 1, + "reEvaluable": true, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "reevaluable-schema-mixed", + "type": "schema", + "detail": { + "id": "test-schema-id", + "schema": "https://ns.adobe.com/personalization/message", + "data": { + "content": "schema-content" + } + } + } + ] + }, + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "non-reevaluable-add-mixed", + "type": "add", + "detail": { + "eventdata": { + "attached_data": { + "addedKey": "addedValue" + } + } + } + } + ] + }, + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "non-reevaluable-schema-mixed", + "type": "schema", + "detail": { + "id": "test-schema-id-2", + "schema": "https://ns.adobe.com/personalization/message", + "data": { + "content": "non-reevaluable-schema" + } + } + } + ] + } + ] +} diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json new file mode 100644 index 000000000..4aeabb3fa --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json @@ -0,0 +1,72 @@ +{ + "version": 1, + "reEvaluable": true, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "reevaluable-schema-1", + "type": "schema", + "detail": { + "id": "test-schema-id-1", + "schema": "https://ns.adobe.com/personalization/message", + "data": { + "messageId": "message-1" + } + } + } + ] + }, + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "reevaluable-schema-2", + "type": "schema", + "detail": { + "id": "test-schema-id-2", + "schema": "https://ns.adobe.com/personalization/message", + "data": { + "messageId": "message-2" + } + } + } + ] + } + ] +} diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json new file mode 100644 index 000000000..36b308958 --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json @@ -0,0 +1,37 @@ +{ + "version": 1, + "reEvaluable": true, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "reevaluable-add-1", + "type": "add", + "detail": { + "eventdata": { + "addedKey": "addedValue" + } + } + } + ] + } + ] +} diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json new file mode 100644 index 000000000..ea5aea4d6 --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json @@ -0,0 +1,50 @@ +{ + "version": 1, + "reEvaluable": true, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + }, + { + "type": "matcher", + "definition": { + "key": "~source", + "matcher": "eq", + "values": [ + "com.adobe.eventSource.requestContent" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "reevaluable-schema-1", + "type": "schema", + "detail": { + "id": "test-schema-id", + "schema": "https://ns.adobe.com/personalization/message", + "data": { + "content": "test-content", + "key": "value" + } + } + } + ] + } + ] +} diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json new file mode 100644 index 000000000..4a391b69c --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json @@ -0,0 +1,50 @@ +{ + "version": 1, + "reEvaluable": true, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.generic" + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "mixed-rule-schema", + "type": "schema", + "detail": { + "id": "test-schema-id", + "schema": "https://ns.adobe.com/personalization/message", + "data": { + "content": "schema-content" + } + } + }, + { + "id": "mixed-rule-add", + "type": "add", + "detail": { + "eventdata": { + "attached_data": { + "mixedRuleKey": "mixedRuleValue" + } + } + } + } + ] + } + ] +} From 6882148973d18053263f574fbbc3e1e1d2054ce2 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Thu, 22 Jan 2026 15:12:50 +0530 Subject: [PATCH 08/23] Updating parsing and unit test cases --- .../mobile/launch/rulesengine/LaunchRule.kt | 2 +- .../rulesengine/LaunchRulesConsequence.kt | 2 +- .../mobile/launch/rulesengine/RuleMeta.kt | 23 ++++++++++ .../launch/rulesengine/json/JSONMeta.kt | 45 +++++++++++++++++++ .../launch/rulesengine/json/JSONRule.kt | 12 +++-- .../launch/rulesengine/json/JSONRuleRoot.kt | 8 ++-- .../rulesengine/LaunchRuleJavaTests.java | 10 ++--- .../launch/rulesengine/LaunchRuleTests.kt | 10 ++--- .../LaunchRulesEngineReevaluationTests.kt | 13 +++--- .../launch/rulesengine/json/JSONRuleTests.kt | 4 +- ...les_testNonReevaluable_addConsequence.json | 6 ++- ..._testNonReevaluable_schemaConsequence.json | 6 ++- ...les_testReevaluable_eventModification.json | 11 +++-- .../rules_testReevaluable_mixedRules.json | 6 ++- ...s_testReevaluable_multipleSchemaRules.json | 11 +++-- ..._testReevaluable_nonSchemaConsequence.json | 6 ++- ...les_testReevaluable_schemaConsequence.json | 6 ++- ...evaluable_singleRuleMixedConsequences.json | 6 ++- 18 files changed, 139 insertions(+), 48 deletions(-) create mode 100644 code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt create mode 100644 code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt index a02606c37..175af4846 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt @@ -25,7 +25,7 @@ import com.adobe.marketing.mobile.rulesengine.Rule data class LaunchRule @JvmOverloads constructor( val condition: Evaluable, val consequenceList: List, - val reevaluable: Boolean = false + val meta: RuleMeta = RuleMeta(false) ) : Rule { override fun getEvaluable(): Evaluable { return condition diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt index 130352af9..35761c6b8 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt @@ -175,7 +175,7 @@ internal class LaunchRulesConsequence( fun getReevaluableRules(rules: MutableList): MutableList { val revaluableRules: MutableList = java.util.ArrayList() for (rule in rules) { - if (rule.reevaluable && rule.hasReevaluableSupportedConsequence) { + if (rule.meta.reEvaluable && rule.hasReevaluableSupportedConsequence) { revaluableRules.add(rule) } } diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt new file mode 100644 index 000000000..49f6857e2 --- /dev/null +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt @@ -0,0 +1,23 @@ +/* + Copyright 2022 Adobe. All rights reserved. + This file is licensed to you under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. You may obtain a copy + of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under + the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + OF ANY KIND, either express or implied. See the License for the specific language + governing permissions and limitations under the License. +*/ + +package com.adobe.marketing.mobile.launch.rulesengine + +/** + * The data class representing a rule's consequence object + * + * @property reEvaluable the flag reEvaluable for sensitive rules + * @constructor Constructs a new [RuleMeta] + */ + +data class RuleMeta( + val reEvaluable: Boolean, +) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt new file mode 100644 index 000000000..149fa88a4 --- /dev/null +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt @@ -0,0 +1,45 @@ +/* + Copyright 2022 Adobe. All rights reserved. + This file is licensed to you under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. You may obtain a copy + of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under + the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + OF ANY KIND, either express or implied. See the License for the specific language + governing permissions and limitations under the License. +*/ + +package com.adobe.marketing.mobile.launch.rulesengine.json + +import com.adobe.marketing.mobile.internal.CoreConstants +import com.adobe.marketing.mobile.internal.util.toMap +import com.adobe.marketing.mobile.launch.rulesengine.RuleConsequence +import com.adobe.marketing.mobile.launch.rulesengine.RuleMeta +import com.adobe.marketing.mobile.services.Log +import org.json.JSONObject + +/** + * The class representing a Rule's consequence + */ +internal class JSONMeta private constructor( + private val reEvaluable: Boolean, +) { + companion object { + private const val KEY_REEVALUABLE = "reEvaluable" + operator fun invoke(jsonObject: JSONObject?): JSONMeta { + return JSONMeta( + jsonObject?.optBoolean(KEY_REEVALUABLE, false) ?: false + ) + } + } + + /** + * Converts this object into a validated [RuleMeta]. + * + * @return a valid [RuleMeta] or `null` if any validation check fails + */ + @JvmSynthetic + internal fun toMeta(): RuleMeta { + return RuleMeta(reEvaluable) + } +} diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRule.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRule.kt index fe45b9b06..cd591ddfe 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRule.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRule.kt @@ -25,13 +25,15 @@ import org.json.JSONObject */ internal class JSONRule private constructor( val condition: JSONObject, - val consequences: JSONArray + val consequences: JSONArray, + val meta: JSONObject? ) { companion object { private const val LOG_TAG = "JSONRule" private const val KEY_CONDITION = "condition" private const val KEY_CONSEQUENCES = "consequences" + private const val KEY_META = "meta" /** * Optionally constructs a new [JSONRule] @@ -43,6 +45,7 @@ internal class JSONRule private constructor( if (jsonObject !is JSONObject) return null val condition = jsonObject.getJSONObject(KEY_CONDITION) val consequences = jsonObject.getJSONArray(KEY_CONSEQUENCES) + val meta = jsonObject.optJSONObject(KEY_META) if (condition !is JSONObject || consequences !is JSONArray) { Log.error( LaunchRulesEngineConstants.LOG_TAG, @@ -51,7 +54,7 @@ internal class JSONRule private constructor( ) return null } - return JSONRule(condition, consequences) + return JSONRule(condition, consequences, meta) } } @@ -61,7 +64,7 @@ internal class JSONRule private constructor( * @return an object of [LaunchRule] */ @JvmSynthetic - internal fun toLaunchRule(extensionApi: ExtensionApi, reEvaluable: Boolean): LaunchRule? { + internal fun toLaunchRule(extensionApi: ExtensionApi): LaunchRule? { val evaluable = JSONCondition.build(condition, extensionApi)?.toEvaluable() if (evaluable !is Evaluable) { Log.error( @@ -74,6 +77,7 @@ internal class JSONRule private constructor( val consequenceList = consequences.map { JSONConsequence(it as? JSONObject)?.toRuleConsequence() ?: throw Exception() } - return LaunchRule(evaluable, consequenceList, reEvaluable) + val metaObject = JSONMeta(meta).toMeta() + return LaunchRule(evaluable, consequenceList, metaObject) } } diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleRoot.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleRoot.kt index 8e3b19d40..565373c02 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleRoot.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleRoot.kt @@ -22,16 +22,14 @@ import org.json.JSONObject /** * The class representing a set of Rules */ -internal class JSONRuleRoot private constructor(val version: String, val jsonArray: JSONArray, val reEvaluable: Boolean) { +internal class JSONRuleRoot private constructor(val version: String, val jsonArray: JSONArray) { companion object { private const val LOG_TAG = "JSONRuleRoot" private const val KEY_VERSION = "version" private const val KEY_RULES = "rules" - private const val KEY_REEVALUABLE = "reEvaluable" operator fun invoke(jsonObject: JSONObject): JSONRuleRoot? { val version = jsonObject.optString(KEY_VERSION, "0") val rules = jsonObject.optJSONArray(KEY_RULES) - val reeValuable = jsonObject.optBoolean(KEY_REEVALUABLE, false) if (rules !is JSONArray) { Log.error( LaunchRulesEngineConstants.LOG_TAG, @@ -40,7 +38,7 @@ internal class JSONRuleRoot private constructor(val version: String, val jsonArr ) return null } - return JSONRuleRoot(version, rules, reeValuable) + return JSONRuleRoot(version, rules) } } @@ -52,7 +50,7 @@ internal class JSONRuleRoot private constructor(val version: String, val jsonArr @JvmSynthetic fun toLaunchRules(extensionApi: ExtensionApi): List { return jsonArray.map { - JSONRule(it as? JSONObject)?.toLaunchRule(extensionApi, reEvaluable) ?: throw Exception() + JSONRule(it as? JSONObject)?.toLaunchRule(extensionApi) ?: throw Exception() } } } diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java index cca5bcfda..ebb727025 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java @@ -18,21 +18,21 @@ public class LaunchRuleJavaTests { @Test public void testLaunchRuleCreationWithoutRevaluable() { final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); - assertFalse(rule.getReevaluable()); + assertFalse(rule.getMeta().getReEvaluable()); assertEquals(mockEvaluable, rule.getEvaluable()); } @Test public void testLaunchRuleCreationWithRevaluableFalse() { - final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence), false); - assertFalse(rule.getReevaluable()); + final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); + assertFalse(rule.getMeta().getReEvaluable()); assertEquals(mockEvaluable, rule.getEvaluable()); } @Test public void testLaunchRuleCreationWithRevaluableTrue() { - final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence), true); - assertTrue(rule.getReevaluable()); + final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence), new RuleMeta(true)); + assertTrue(rule.getMeta().getReEvaluable()); assertEquals(mockEvaluable, rule.getEvaluable()); } } \ No newline at end of file diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt index 1ea697052..57ffee4c3 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt @@ -15,21 +15,21 @@ class LaunchRuleTests { @Test fun `test LaunchRule creation without revaluable parameter`() { val rule = LaunchRule(mockEvaluable, listOf(mockConsequence)) - assertFalse(rule.reevaluable) + assertFalse(rule.meta.reEvaluable) assertEquals(mockEvaluable, rule.evaluable) } @Test fun `test LaunchRule creation with revaluable as false`() { - val rule = LaunchRule(mockEvaluable, listOf(mockConsequence), false) - assertFalse(rule.reevaluable) + val rule = LaunchRule(mockEvaluable, listOf(mockConsequence)) + assertFalse(rule.meta.reEvaluable) assertEquals(mockEvaluable, rule.evaluable) } @Test fun `test LaunchRule creation with revaluable as true`() { - val rule = LaunchRule(mockEvaluable, listOf(mockConsequence), true) - assertTrue(rule.reevaluable) + val rule = LaunchRule(mockEvaluable, listOf(mockConsequence), RuleMeta(true)) + assertTrue(rule.meta.reEvaluable) assertEquals(mockEvaluable, rule.evaluable) } } \ No newline at end of file diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt index c38badc08..18ebe8b72 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt @@ -83,7 +83,7 @@ class LaunchRulesEngineReevaluationTests { assertEquals(testEvent.uniqueIdentifier, eventCaptor.firstValue.uniqueIdentifier) assertEquals(1, rulesCaptor.firstValue.size) - assertTrue(rulesCaptor.firstValue[0].reevaluable) + assertTrue(rulesCaptor.firstValue[0].meta.reEvaluable) assertEquals("schema", rulesCaptor.firstValue[0].consequenceList[0].type) } @@ -237,9 +237,8 @@ class LaunchRulesEngineReevaluationTests { // Only the 2 reevaluable schema rules should be passed to interceptor // The add rules (both reevaluable and non-reevaluable) should NOT be in the list - assertEquals(2, rulesCaptor.firstValue.size) - assertTrue(rulesCaptor.firstValue[0].reevaluable) - assertTrue(rulesCaptor.firstValue[1].reevaluable) + assertEquals(1, rulesCaptor.firstValue.size) + assertTrue(rulesCaptor.firstValue[0].meta.reEvaluable) // Verify that BOTH add rule consequences were processed immediately // (Even the reevaluable one, because add is not a reevaluable-supported consequence type) @@ -283,7 +282,7 @@ class LaunchRulesEngineReevaluationTests { // The single reevaluable rule should be passed to interceptor assertEquals(1, rulesCaptor.firstValue.size) - assertTrue(rulesCaptor.firstValue[0].reevaluable) + assertTrue(rulesCaptor.firstValue[0].meta.reEvaluable) // Verify the rule has both consequences assertEquals(2, rulesCaptor.firstValue[0].consequenceList.size) @@ -355,7 +354,7 @@ class LaunchRulesEngineReevaluationTests { // Both reevaluable rules should be passed assertEquals(2, rulesCaptor.firstValue.size) - assertTrue(rulesCaptor.firstValue.all { it.reevaluable }) + assertTrue(rulesCaptor.firstValue.all { it.meta.reEvaluable }) } // ======================================== @@ -578,7 +577,7 @@ class LaunchRulesEngineReevaluationTests { val reevaluableRules = rulesCaptor.firstValue // Only the reevaluable schema rule should be in the list assertEquals(2, reevaluableRules.size) - assertTrue(reevaluableRules[0].reevaluable) + assertTrue(reevaluableRules[0].meta.reEvaluable) assertTrue(reevaluableRules[0].consequenceList.any { it.type == "schema" }) } diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt index 44391e5fc..3d7da0f17 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt @@ -64,12 +64,12 @@ class JSONRuleTests { """.trimIndent() val jsonRule = JSONRule(buildJSONObject(jsonString)) assertTrue(jsonRule is JSONRule) - val launchRule = jsonRule.toLaunchRule(extensionApi, false) + val launchRule = jsonRule.toLaunchRule(extensionApi) assertTrue(launchRule is LaunchRule) assertEquals(1, launchRule.consequenceList.size) assertEquals("pb", launchRule.consequenceList[0].type) assertTrue(launchRule.condition is ComparisonExpression<*, *>) - assertFalse(launchRule.reevaluable) + assertFalse(launchRule.meta.reEvaluable) } @Test diff --git a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json index 07df65959..a30ee9ef4 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json @@ -1,6 +1,5 @@ { "version": 1, - "reEvaluable": false, "rules": [ { "condition": { @@ -33,7 +32,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": false + } } ] } diff --git a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json index 6dce287e8..0e0fcdf0b 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json @@ -1,6 +1,5 @@ { "version": 1, - "reEvaluable": false, "rules": [ { "condition": { @@ -33,7 +32,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": false + } } ] } diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json index b03acba08..4857c5cd4 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json @@ -1,6 +1,5 @@ { "version": 1, - "reEvaluable": true, "rules": [ { "condition": { @@ -32,7 +31,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": false + } }, { "condition": { @@ -65,7 +67,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": true + } } ] } diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json index dac7f0b6d..38eec67a0 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json @@ -1,6 +1,5 @@ { "version": 1, - "reEvaluable": true, "rules": [ { "condition": { @@ -33,7 +32,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": true + } }, { "condition": { diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json index 4aeabb3fa..fce9fafe1 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json @@ -1,6 +1,5 @@ { "version": 1, - "reEvaluable": true, "rules": [ { "condition": { @@ -33,7 +32,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": true + } }, { "condition": { @@ -66,7 +68,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": true + } } ] } diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json index 36b308958..9a465f41c 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json @@ -1,6 +1,5 @@ { "version": 1, - "reEvaluable": true, "rules": [ { "condition": { @@ -31,7 +30,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": true + } } ] } diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json index ea5aea4d6..2374c3750 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json @@ -1,6 +1,5 @@ { "version": 1, - "reEvaluable": true, "rules": [ { "condition": { @@ -44,7 +43,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": true + } } ] } diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json index 4a391b69c..e00cb21fc 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json @@ -1,6 +1,5 @@ { "version": 1, - "reEvaluable": true, "rules": [ { "condition": { @@ -44,7 +43,10 @@ } } } - ] + ], + "meta": { + "reEvaluable": true + } } ] } From 278eae05979fb154fe447e39043fac77a755f831 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Wed, 28 Jan 2026 11:51:38 +0530 Subject: [PATCH 09/23] Adding a default value for reEvaluable flag in Meta Object --- .../com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt | 2 +- .../com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt index 175af4846..45a5a0000 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt @@ -25,7 +25,7 @@ import com.adobe.marketing.mobile.rulesengine.Rule data class LaunchRule @JvmOverloads constructor( val condition: Evaluable, val consequenceList: List, - val meta: RuleMeta = RuleMeta(false) + val meta: RuleMeta = RuleMeta() ) : Rule { override fun getEvaluable(): Evaluable { return condition diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt index 49f6857e2..273b90e3c 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt @@ -19,5 +19,5 @@ package com.adobe.marketing.mobile.launch.rulesengine */ data class RuleMeta( - val reEvaluable: Boolean, + val reEvaluable: Boolean = false, ) From df0a37ef8d04bfd0207819e00d9808f7180e58f4 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Thu, 29 Jan 2026 23:43:47 +0530 Subject: [PATCH 10/23] Updating reevaluation key in meta object from "reEvaluable" to "reEvaluate" --- .../mobile/launch/rulesengine/LaunchRule.kt | 2 +- .../rulesengine/LaunchRulesConsequence.kt | 2 +- .../mobile/launch/rulesengine/RuleMeta.kt | 4 ++-- .../launch/rulesengine/json/JSONMeta.kt | 12 ++++------ .../rulesengine/LaunchRuleJavaTests.java | 6 ++--- .../launch/rulesengine/LaunchRuleTests.kt | 6 ++--- .../LaunchRulesEngineReevaluationTests.kt | 22 +++++++++---------- .../launch/rulesengine/json/JSONRuleTests.kt | 2 +- ...les_testNonReevaluable_addConsequence.json | 2 +- ..._testNonReevaluable_schemaConsequence.json | 2 +- ...les_testReevaluable_eventModification.json | 4 ++-- .../rules_testReevaluable_mixedRules.json | 2 +- ...s_testReevaluable_multipleSchemaRules.json | 4 ++-- ..._testReevaluable_nonSchemaConsequence.json | 2 +- ...les_testReevaluable_schemaConsequence.json | 2 +- ...evaluable_singleRuleMixedConsequences.json | 2 +- 16 files changed, 36 insertions(+), 40 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt index 45a5a0000..a6484cef1 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt @@ -19,7 +19,7 @@ import com.adobe.marketing.mobile.rulesengine.Rule * * @property condition an object of [Evaluable] * @property consequenceList a list of [RuleConsequence] objects - * @property reevaluable a boolean indicating if the rule should be reevaluated + * @property meta an object containing relevant meta data regarding the rule * @constructor Constructs a new [LaunchRule] */ data class LaunchRule @JvmOverloads constructor( diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt index 35761c6b8..22dba2ad5 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt @@ -175,7 +175,7 @@ internal class LaunchRulesConsequence( fun getReevaluableRules(rules: MutableList): MutableList { val revaluableRules: MutableList = java.util.ArrayList() for (rule in rules) { - if (rule.meta.reEvaluable && rule.hasReevaluableSupportedConsequence) { + if (rule.meta.reEvaluate && rule.hasReevaluableSupportedConsequence) { revaluableRules.add(rule) } } diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt index 273b90e3c..cc80266a6 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleMeta.kt @@ -14,10 +14,10 @@ package com.adobe.marketing.mobile.launch.rulesengine /** * The data class representing a rule's consequence object * - * @property reEvaluable the flag reEvaluable for sensitive rules + * @property reEvaluate the flag reEvaluate for sensitive rules * @constructor Constructs a new [RuleMeta] */ data class RuleMeta( - val reEvaluable: Boolean = false, + val reEvaluate: Boolean = false, ) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt index 149fa88a4..88ad44055 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt @@ -11,24 +11,20 @@ package com.adobe.marketing.mobile.launch.rulesengine.json -import com.adobe.marketing.mobile.internal.CoreConstants -import com.adobe.marketing.mobile.internal.util.toMap -import com.adobe.marketing.mobile.launch.rulesengine.RuleConsequence import com.adobe.marketing.mobile.launch.rulesengine.RuleMeta -import com.adobe.marketing.mobile.services.Log import org.json.JSONObject /** * The class representing a Rule's consequence */ internal class JSONMeta private constructor( - private val reEvaluable: Boolean, + private val reEvaluate: Boolean, ) { companion object { - private const val KEY_REEVALUABLE = "reEvaluable" + private const val KEY_REEVALUATE = "reEvaluate" operator fun invoke(jsonObject: JSONObject?): JSONMeta { return JSONMeta( - jsonObject?.optBoolean(KEY_REEVALUABLE, false) ?: false + jsonObject?.optBoolean(KEY_REEVALUATE, false) ?: false ) } } @@ -40,6 +36,6 @@ internal class JSONMeta private constructor( */ @JvmSynthetic internal fun toMeta(): RuleMeta { - return RuleMeta(reEvaluable) + return RuleMeta(reEvaluate) } } diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java index ebb727025..38e4e73ff 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java @@ -18,21 +18,21 @@ public class LaunchRuleJavaTests { @Test public void testLaunchRuleCreationWithoutRevaluable() { final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); - assertFalse(rule.getMeta().getReEvaluable()); + assertFalse(rule.getMeta().getReEvaluate()); assertEquals(mockEvaluable, rule.getEvaluable()); } @Test public void testLaunchRuleCreationWithRevaluableFalse() { final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); - assertFalse(rule.getMeta().getReEvaluable()); + assertFalse(rule.getMeta().getReEvaluate()); assertEquals(mockEvaluable, rule.getEvaluable()); } @Test public void testLaunchRuleCreationWithRevaluableTrue() { final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence), new RuleMeta(true)); - assertTrue(rule.getMeta().getReEvaluable()); + assertTrue(rule.getMeta().getReEvaluate()); assertEquals(mockEvaluable, rule.getEvaluable()); } } \ No newline at end of file diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt index 57ffee4c3..0ab546d09 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt @@ -15,21 +15,21 @@ class LaunchRuleTests { @Test fun `test LaunchRule creation without revaluable parameter`() { val rule = LaunchRule(mockEvaluable, listOf(mockConsequence)) - assertFalse(rule.meta.reEvaluable) + assertFalse(rule.meta.reEvaluate) assertEquals(mockEvaluable, rule.evaluable) } @Test fun `test LaunchRule creation with revaluable as false`() { val rule = LaunchRule(mockEvaluable, listOf(mockConsequence)) - assertFalse(rule.meta.reEvaluable) + assertFalse(rule.meta.reEvaluate) assertEquals(mockEvaluable, rule.evaluable) } @Test fun `test LaunchRule creation with revaluable as true`() { val rule = LaunchRule(mockEvaluable, listOf(mockConsequence), RuleMeta(true)) - assertTrue(rule.meta.reEvaluable) + assertTrue(rule.meta.reEvaluate) assertEquals(mockEvaluable, rule.evaluable) } } \ No newline at end of file diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt index 18ebe8b72..f97d27e8d 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt @@ -83,12 +83,12 @@ class LaunchRulesEngineReevaluationTests { assertEquals(testEvent.uniqueIdentifier, eventCaptor.firstValue.uniqueIdentifier) assertEquals(1, rulesCaptor.firstValue.size) - assertTrue(rulesCaptor.firstValue[0].meta.reEvaluable) + assertTrue(rulesCaptor.firstValue[0].meta.reEvaluate) assertEquals("schema", rulesCaptor.firstValue[0].consequenceList[0].type) } @Test - fun `Test reevaluation interceptor is NOT triggered when reevaluable is false`() { + fun `Test reevaluation interceptor is NOT triggered when reEvaluate is false`() { val json = readTestResources("rules_module_tests/rules_testNonReevaluable_schemaConsequence.json") assertNotNull(json) val rules = JSONRulesParser.parse(json, extensionApi) @@ -198,10 +198,10 @@ class LaunchRulesEngineReevaluationTests { // 2. Reevaluable rules with add consequences -> processed immediately (add is not a reevaluable-supported type) // 3. Non-reevaluable rules with add consequences -> processed immediately - val reEvaluableRuleFile = readTestResources("rules_module_tests/rules_testReevaluable_mixedRules.json") - assertNotNull(reEvaluableRuleFile) - val reEvaluableRules = JSONRulesParser.parse(reEvaluableRuleFile, extensionApi) - // This file has 3 rules (all with reEvaluable=true): + val reEvaluateRuleFile = readTestResources("rules_module_tests/rules_testReevaluable_mixedRules.json") + assertNotNull(reEvaluateRuleFile) + val reEvaluateRules = JSONRulesParser.parse(reEvaluateRuleFile, extensionApi) + // This file has 3 rules (all with reEvaluate=true): // - Rule 1: schema consequence (will be held) // - Rule 2: add consequence (will process immediately - add is not a reevaluable-supported type) // - Rule 3: schema consequence (will be held) @@ -212,7 +212,7 @@ class LaunchRulesEngineReevaluationTests { val addRule = JSONRulesParser.parse(addRuleFile, extensionApi) assertNotNull(addRule) - launchRulesEngine.replaceRules(reEvaluableRules) + launchRulesEngine.replaceRules(reEvaluateRules) launchRulesEngine.addRules(addRule) val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) @@ -238,7 +238,7 @@ class LaunchRulesEngineReevaluationTests { // Only the 2 reevaluable schema rules should be passed to interceptor // The add rules (both reevaluable and non-reevaluable) should NOT be in the list assertEquals(1, rulesCaptor.firstValue.size) - assertTrue(rulesCaptor.firstValue[0].meta.reEvaluable) + assertTrue(rulesCaptor.firstValue[0].meta.reEvaluate) // Verify that BOTH add rule consequences were processed immediately // (Even the reevaluable one, because add is not a reevaluable-supported consequence type) @@ -282,7 +282,7 @@ class LaunchRulesEngineReevaluationTests { // The single reevaluable rule should be passed to interceptor assertEquals(1, rulesCaptor.firstValue.size) - assertTrue(rulesCaptor.firstValue[0].meta.reEvaluable) + assertTrue(rulesCaptor.firstValue[0].meta.reEvaluate) // Verify the rule has both consequences assertEquals(2, rulesCaptor.firstValue[0].consequenceList.size) @@ -354,7 +354,7 @@ class LaunchRulesEngineReevaluationTests { // Both reevaluable rules should be passed assertEquals(2, rulesCaptor.firstValue.size) - assertTrue(rulesCaptor.firstValue.all { it.meta.reEvaluable }) + assertTrue(rulesCaptor.firstValue.all { it.meta.reEvaluate }) } // ======================================== @@ -577,7 +577,7 @@ class LaunchRulesEngineReevaluationTests { val reevaluableRules = rulesCaptor.firstValue // Only the reevaluable schema rule should be in the list assertEquals(2, reevaluableRules.size) - assertTrue(reevaluableRules[0].meta.reEvaluable) + assertTrue(reevaluableRules[0].meta.reEvaluate) assertTrue(reevaluableRules[0].consequenceList.any { it.type == "schema" }) } diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt index 3d7da0f17..eeb04ad9f 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt @@ -69,7 +69,7 @@ class JSONRuleTests { assertEquals(1, launchRule.consequenceList.size) assertEquals("pb", launchRule.consequenceList[0].type) assertTrue(launchRule.condition is ComparisonExpression<*, *>) - assertFalse(launchRule.meta.reEvaluable) + assertFalse(launchRule.meta.reEvaluate) } @Test diff --git a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json index a30ee9ef4..7b024f47b 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_addConsequence.json @@ -34,7 +34,7 @@ } ], "meta": { - "reEvaluable": false + "reEvaluate": false } } ] diff --git a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json index 0e0fcdf0b..9759bc9c6 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testNonReevaluable_schemaConsequence.json @@ -34,7 +34,7 @@ } ], "meta": { - "reEvaluable": false + "reEvaluate": false } } ] diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json index 4857c5cd4..63a8b5148 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_eventModification.json @@ -33,7 +33,7 @@ } ], "meta": { - "reEvaluable": false + "reEvaluate": false } }, { @@ -69,7 +69,7 @@ } ], "meta": { - "reEvaluable": true + "reEvaluate": true } } ] diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json index 38eec67a0..777b84b4d 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_mixedRules.json @@ -34,7 +34,7 @@ } ], "meta": { - "reEvaluable": true + "reEvaluate": true } }, { diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json index fce9fafe1..7549b40ab 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_multipleSchemaRules.json @@ -34,7 +34,7 @@ } ], "meta": { - "reEvaluable": true + "reEvaluate": true } }, { @@ -70,7 +70,7 @@ } ], "meta": { - "reEvaluable": true + "reEvaluate": true } } ] diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json index 9a465f41c..c9412573a 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_nonSchemaConsequence.json @@ -32,7 +32,7 @@ } ], "meta": { - "reEvaluable": true + "reEvaluate": true } } ] diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json index 2374c3750..38481991a 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_schemaConsequence.json @@ -45,7 +45,7 @@ } ], "meta": { - "reEvaluable": true + "reEvaluate": true } } ] diff --git a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json index e00cb21fc..f3d7ee20b 100644 --- a/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json +++ b/code/core/src/test/resources/rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json @@ -45,7 +45,7 @@ } ], "meta": { - "reEvaluable": true + "reEvaluate": true } } ] From 8c0df490b4b42e0702b7debce788958902c015ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 2 Feb 2026 08:25:28 +0000 Subject: [PATCH 11/23] Updating Core version to 3.5.1 --- .../java/com/adobe/marketing/mobile/internal/CoreConstants.kt | 2 +- code/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt index f548dba69..b8d667721 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt @@ -13,7 +13,7 @@ package com.adobe.marketing.mobile.internal internal object CoreConstants { const val LOG_TAG = "MobileCore" - const val VERSION = "3.5.0" + const val VERSION = "3.5.1" object EventDataKeys { /** diff --git a/code/gradle.properties b/code/gradle.properties index de0242d3d..883c6a22b 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -5,7 +5,7 @@ android.useAndroidX=true #Maven artifacts #Core extension -coreExtensionVersion=3.5.0 +coreExtensionVersion=3.5.1 coreExtensionName=core coreMavenRepoName=AdobeMobileCoreSdk coreMavenRepoDescription=Android Core Extension for Adobe Mobile Marketing From 6263b92fd93283815be22dd9c368954883a9b1b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 4 Feb 2026 06:31:31 +0000 Subject: [PATCH 12/23] Updating Core version to 3.6.0 --- .../java/com/adobe/marketing/mobile/internal/CoreConstants.kt | 2 +- code/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt index b8d667721..daadaaa07 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt @@ -13,7 +13,7 @@ package com.adobe.marketing.mobile.internal internal object CoreConstants { const val LOG_TAG = "MobileCore" - const val VERSION = "3.5.1" + const val VERSION = "3.6.0" object EventDataKeys { /** diff --git a/code/gradle.properties b/code/gradle.properties index 883c6a22b..bde0ddad9 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -5,7 +5,7 @@ android.useAndroidX=true #Maven artifacts #Core extension -coreExtensionVersion=3.5.1 +coreExtensionVersion=3.6.0 coreExtensionName=core coreMavenRepoName=AdobeMobileCoreSdk coreMavenRepoDescription=Android Core Extension for Adobe Mobile Marketing From a28d476d5890f66a7e6d1eb413c012a2a4edbf37 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Wed, 4 Feb 2026 19:02:05 +0530 Subject: [PATCH 13/23] Spotless Apply --- .../rulesengine/LaunchRulesConsequence.kt | 5 ++-- .../launch/rulesengine/LaunchRulesEngine.java | 16 +++++----- .../rulesengine/LaunchRuleJavaTests.java | 30 ++++++++++++++----- .../launch/rulesengine/LaunchRuleTests.kt | 13 +++++++- .../LaunchRulesEngineReevaluationTests.kt | 26 ++++++++-------- .../launch/rulesengine/json/JSONRuleTests.kt | 2 +- 6 files changed, 60 insertions(+), 32 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt index 22dba2ad5..fcc0ea13e 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt @@ -26,7 +26,6 @@ import com.adobe.marketing.mobile.internal.util.fnv1a32 import com.adobe.marketing.mobile.internal.util.prettify import com.adobe.marketing.mobile.internal.util.toEventHistoryRequest import com.adobe.marketing.mobile.rulesengine.DelimiterPair -import com.adobe.marketing.mobile.rulesengine.Rule import com.adobe.marketing.mobile.rulesengine.Template import com.adobe.marketing.mobile.rulesengine.TokenFinder import com.adobe.marketing.mobile.services.Log @@ -185,7 +184,7 @@ internal class LaunchRulesConsequence( fun getRulesToHoldForReevaluation(rules: MutableList): MutableList { val rulesToHold: MutableList = ArrayList() for (rule in rules) { - if(rule.hasReevaluableSupportedConsequence){ + if (rule.hasReevaluableSupportedConsequence) { rulesToHold.add(rule) } } @@ -618,4 +617,4 @@ private val RuleConsequence.schema: String? get() = detail["schema"] as? String private val RuleConsequence.detailData: Map? - get() = DataReader.optTypedMap(Any::class.java, detail, "data", null) \ No newline at end of file + get() = DataReader.optTypedMap(Any::class.java, detail, "data", null) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index baed8397a..3e0901ea0 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -33,9 +33,9 @@ public interface CompletionCallback { } /** - * An interface for an interceptor that is triggered when a {@link LaunchRule} with the re-evaluation - * flag is triggered. The interceptor is responsible for updating the rules and invoking the {@link - * CompletionCallback} when complete. + * An interface for an interceptor that is triggered when a {@link LaunchRule} with the + * re-evaluation flag is triggered. The interceptor is responsible for updating the rules and + * invoking the {@link CompletionCallback} when complete. */ public interface RuleReevaluationInterceptor { void onReevaluationTriggered( @@ -119,8 +119,8 @@ public void addRules(final List rules) { /** * Processes the supplied event with all the rules that match it. This processing may result in - * dispatch of the supplied event after attachment, modification of its data or dispatch of a new - * event from the supplied event. + * dispatch of the supplied event after attachment, modification of its data or dispatch of a + * new event from the supplied event. * * @param event the event to be evaluated * @return the processed [Event] after token replacement. @@ -167,11 +167,13 @@ private Event processAndIntercept(final Event event) { if (reevaluationInterceptor == null) { return launchRulesConsequence.process(event, matchedRules); } else { - final List revaluableRules = launchRulesConsequence.getReevaluableRules(matchedRules); + final List revaluableRules = + launchRulesConsequence.getReevaluableRules(matchedRules); if (revaluableRules.isEmpty()) { return launchRulesConsequence.process(event, matchedRules); } else { - final List rulesToHold = launchRulesConsequence.getRulesToHoldForReevaluation(matchedRules); + final List rulesToHold = + launchRulesConsequence.getRulesToHoldForReevaluation(matchedRules); final ArrayList rulesToProcess = new ArrayList<>(matchedRules); rulesToProcess.removeAll(rulesToHold); Event processedEvent = launchRulesConsequence.process(event, rulesToProcess); diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java index 38e4e73ff..83ead7557 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleJavaTests.java @@ -1,14 +1,24 @@ +/* + Copyright 2026 Adobe. All rights reserved. + This file is licensed to you under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. You may obtain a copy + of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under + the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + OF ANY KIND, either express or implied. See the License for the specific language + governing permissions and limitations under the License. +*/ + package com.adobe.marketing.mobile.launch.rulesengine; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; -import com.adobe.marketing.mobile.rulesengine.Evaluable; - -import org.junit.Test; +import com.adobe.marketing.mobile.rulesengine.Evaluable; import java.util.Collections; +import org.junit.Test; public class LaunchRuleJavaTests { @@ -17,22 +27,28 @@ public class LaunchRuleJavaTests { @Test public void testLaunchRuleCreationWithoutRevaluable() { - final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); + final LaunchRule rule = + new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); assertFalse(rule.getMeta().getReEvaluate()); assertEquals(mockEvaluable, rule.getEvaluable()); } @Test public void testLaunchRuleCreationWithRevaluableFalse() { - final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); + final LaunchRule rule = + new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence)); assertFalse(rule.getMeta().getReEvaluate()); assertEquals(mockEvaluable, rule.getEvaluable()); } @Test public void testLaunchRuleCreationWithRevaluableTrue() { - final LaunchRule rule = new LaunchRule(mockEvaluable, Collections.singletonList(mockConsequence), new RuleMeta(true)); + final LaunchRule rule = + new LaunchRule( + mockEvaluable, + Collections.singletonList(mockConsequence), + new RuleMeta(true)); assertTrue(rule.getMeta().getReEvaluate()); assertEquals(mockEvaluable, rule.getEvaluable()); } -} \ No newline at end of file +} diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt index 0ab546d09..59fd032c5 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRuleTests.kt @@ -1,3 +1,14 @@ +/* + Copyright 2026 Adobe. All rights reserved. + This file is licensed to you under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. You may obtain a copy + of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under + the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + OF ANY KIND, either express or implied. See the License for the specific language + governing permissions and limitations under the License. +*/ + package com.adobe.marketing.mobile.launch.rulesengine import com.adobe.marketing.mobile.rulesengine.Evaluable @@ -32,4 +43,4 @@ class LaunchRuleTests { assertTrue(rule.meta.reEvaluate) assertEquals(mockEvaluable, rule.evaluable) } -} \ No newline at end of file +} diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt index f97d27e8d..742fdf7f0 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt @@ -33,7 +33,7 @@ import kotlin.test.assertTrue /** * Unit tests for LaunchRulesEngine reevaluation interceptor functionality. - * + * * These tests cover the reevaluation feature which allows rules with schema consequences * to be held back while an interceptor fetches updated rules before processing. */ @@ -178,11 +178,11 @@ class LaunchRulesEngineReevaluationTests { // Should dispatch consequence event normally verify(extensionApi, Mockito.atLeastOnce()).dispatch(eventCaptor.capture()) - + // Verify a consequence event was dispatched val dispatchedEvents = eventCaptor.allValues - val consequenceEvents = dispatchedEvents.filter { - it.type == EventType.RULES_ENGINE && it.source == EventSource.RESPONSE_CONTENT + val consequenceEvents = dispatchedEvents.filter { + it.type == EventType.RULES_ENGINE && it.source == EventSource.RESPONSE_CONTENT } assertTrue(consequenceEvents.isNotEmpty()) } @@ -197,7 +197,7 @@ class LaunchRulesEngineReevaluationTests { // 1. Reevaluable rules with schema consequences -> held // 2. Reevaluable rules with add consequences -> processed immediately (add is not a reevaluable-supported type) // 3. Non-reevaluable rules with add consequences -> processed immediately - + val reEvaluateRuleFile = readTestResources("rules_module_tests/rules_testReevaluable_mixedRules.json") assertNotNull(reEvaluateRuleFile) val reEvaluateRules = JSONRulesParser.parse(reEvaluateRuleFile, extensionApi) @@ -205,13 +205,13 @@ class LaunchRulesEngineReevaluationTests { // - Rule 1: schema consequence (will be held) // - Rule 2: add consequence (will process immediately - add is not a reevaluable-supported type) // - Rule 3: schema consequence (will be held) - + // Load an additional NON-reevaluable add rule val addRuleFile = readTestResources("rules_module_tests/rules_testNonReevaluable_addConsequence.json") assertNotNull(addRuleFile) val addRule = JSONRulesParser.parse(addRuleFile, extensionApi) assertNotNull(addRule) - + launchRulesEngine.replaceRules(reEvaluateRules) launchRulesEngine.addRules(addRule) @@ -254,7 +254,7 @@ class LaunchRulesEngineReevaluationTests { fun `Test single rule with mixed consequences - all consequences held together`() { // This test verifies that if a SINGLE RULE has BOTH schema and non-schema consequences, // the ENTIRE RULE is held (including the non-schema consequences) - + val json = readTestResources("rules_module_tests/rules_testReevaluable_singleRuleMixedConsequences.json") assertNotNull(json) val rules = JSONRulesParser.parse(json, extensionApi) @@ -344,7 +344,7 @@ class LaunchRulesEngineReevaluationTests { launchRulesEngine.processEvent(testEvent) val rulesCaptor: KArgumentCaptor> = argumentCaptor() - + // Interceptor should be called exactly once verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( any(), @@ -394,10 +394,10 @@ class LaunchRulesEngineReevaluationTests { // Verify that consequence event was dispatched after callback verify(extensionApi, Mockito.atLeastOnce()).dispatch(eventCaptor.capture()) - + val dispatchedEvents = eventCaptor.allValues - val consequenceEvents = dispatchedEvents.filter { - it.type == EventType.RULES_ENGINE && it.source == EventSource.RESPONSE_CONTENT + val consequenceEvents = dispatchedEvents.filter { + it.type == EventType.RULES_ENGINE && it.source == EventSource.RESPONSE_CONTENT } assertTrue(consequenceEvents.isNotEmpty()) } @@ -649,7 +649,7 @@ class LaunchRulesEngineReevaluationTests { // Second interceptor should be called verify(mockInterceptor2, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) - + // First interceptor should not be called again verify(mockInterceptor1, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) } diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt index eeb04ad9f..f1a69c2b2 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONRuleTests.kt @@ -22,9 +22,9 @@ import org.junit.runner.RunWith import org.mockito.Mockito import org.mockito.junit.MockitoJUnitRunner import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertNull import kotlin.test.assertTrue -import kotlin.test.assertFalse @RunWith(MockitoJUnitRunner.Silent::class) class JSONRuleTests { From c52ba74a613818c7de7df65670557f8a3b5b0d8a Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Wed, 4 Feb 2026 19:34:06 +0530 Subject: [PATCH 14/23] Fixing Unit Test case assertion --- .../launch/rulesengine/LaunchRulesEngineReevaluationTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt index 742fdf7f0..11551952b 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt @@ -576,7 +576,7 @@ class LaunchRulesEngineReevaluationTests { val reevaluableRules = rulesCaptor.firstValue // Only the reevaluable schema rule should be in the list - assertEquals(2, reevaluableRules.size) + assertEquals(1, reevaluableRules.size) assertTrue(reevaluableRules[0].meta.reEvaluate) assertTrue(reevaluableRules[0].consequenceList.any { it.type == "schema" }) } From 5bff15418e29676fd23a9afbcecc3eedc3b52ee7 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Wed, 4 Feb 2026 19:56:48 +0530 Subject: [PATCH 15/23] Adding API changes declaration to core.api --- code/core/api/core.api | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/code/core/api/core.api b/code/core/api/core.api index 4d9277c3d..1a3cdb45a 100644 --- a/code/core/api/core.api +++ b/code/core/api/core.api @@ -323,14 +323,18 @@ public final class com/adobe/marketing/mobile/WrapperType : java/lang/Enum { public final class com/adobe/marketing/mobile/launch/rulesengine/LaunchRule : com/adobe/marketing/mobile/rulesengine/Rule { public static final field $stable I public fun (Lcom/adobe/marketing/mobile/rulesengine/Evaluable;Ljava/util/List;)V + public fun (Lcom/adobe/marketing/mobile/rulesengine/Evaluable;Ljava/util/List;Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta;)V + public synthetic fun (Lcom/adobe/marketing/mobile/rulesengine/Evaluable;Ljava/util/List;Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lcom/adobe/marketing/mobile/rulesengine/Evaluable; public final fun component2 ()Ljava/util/List; - public final fun copy (Lcom/adobe/marketing/mobile/rulesengine/Evaluable;Ljava/util/List;)Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRule; - public static synthetic fun copy$default (Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRule;Lcom/adobe/marketing/mobile/rulesengine/Evaluable;Ljava/util/List;ILjava/lang/Object;)Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRule; + public final fun component3 ()Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta; + public final fun copy (Lcom/adobe/marketing/mobile/rulesengine/Evaluable;Ljava/util/List;Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta;)Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRule; + public static synthetic fun copy$default (Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRule;Lcom/adobe/marketing/mobile/rulesengine/Evaluable;Ljava/util/List;Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta;ILjava/lang/Object;)Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRule; public fun equals (Ljava/lang/Object;)Z public final fun getCondition ()Lcom/adobe/marketing/mobile/rulesengine/Evaluable; public final fun getConsequenceList ()Ljava/util/List; public fun getEvaluable ()Lcom/adobe/marketing/mobile/rulesengine/Evaluable; + public final fun getMeta ()Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -341,6 +345,15 @@ public class com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine { public fun evaluateEvent (Lcom/adobe/marketing/mobile/Event;)Ljava/util/List; public fun processEvent (Lcom/adobe/marketing/mobile/Event;)Lcom/adobe/marketing/mobile/Event; public fun replaceRules (Ljava/util/List;)V + public fun setRuleReevaluationInterceptor (Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine$RuleReevaluationInterceptor;)V +} + +public abstract interface class com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine$CompletionCallback { + public abstract fun onComplete ()V +} + +public abstract interface class com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine$RuleReevaluationInterceptor { + public abstract fun onReevaluationTriggered (Lcom/adobe/marketing/mobile/Event;Ljava/util/List;Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine$CompletionCallback;)V } public final class com/adobe/marketing/mobile/launch/rulesengine/RuleConsequence { @@ -359,6 +372,20 @@ public final class com/adobe/marketing/mobile/launch/rulesengine/RuleConsequence public fun toString ()Ljava/lang/String; } +public final class com/adobe/marketing/mobile/launch/rulesengine/RuleMeta { + public static final field $stable I + public fun ()V + public fun (Z)V + public synthetic fun (ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun copy (Z)Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta; + public static synthetic fun copy$default (Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta;ZILjava/lang/Object;)Lcom/adobe/marketing/mobile/launch/rulesengine/RuleMeta; + public fun equals (Ljava/lang/Object;)Z + public final fun getReEvaluate ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public class com/adobe/marketing/mobile/launch/rulesengine/download/RulesLoadResult { public fun (Ljava/lang/String;Lcom/adobe/marketing/mobile/launch/rulesengine/download/RulesLoadResult$Reason;)V public fun getData ()Ljava/lang/String; From 1d2880c5cecd6c07c556983203e8d7de1d370f56 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Mon, 9 Feb 2026 22:06:05 +0530 Subject: [PATCH 16/23] Addressing review Comments --- .../mobile/launch/rulesengine/LaunchRule.kt | 2 +- .../launch/rulesengine/LaunchRulesEngine.java | 17 --------------- .../RuleReevaluationInterceptor.kt | 21 +++++++++++++++++++ .../launch/rulesengine/json/JSONMeta.kt | 15 ++++++++++++- 4 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt index a6484cef1..c21b6f63d 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRule.kt @@ -20,7 +20,7 @@ import com.adobe.marketing.mobile.rulesengine.Rule * @property condition an object of [Evaluable] * @property consequenceList a list of [RuleConsequence] objects * @property meta an object containing relevant meta data regarding the rule - * @constructor Constructs a new [LaunchRule] + * @constructor Constructs a new [LaunchRule] (Optional) */ data class LaunchRule @JvmOverloads constructor( val condition: Evaluable, diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index 3e0901ea0..448dea0b8 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -27,23 +27,6 @@ public class LaunchRulesEngine { - /** A callback that should be invoked when the asynchronous work is complete. */ - public interface CompletionCallback { - void onComplete(); - } - - /** - * An interface for an interceptor that is triggered when a {@link LaunchRule} with the - * re-evaluation flag is triggered. The interceptor is responsible for updating the rules and - * invoking the {@link CompletionCallback} when complete. - */ - public interface RuleReevaluationInterceptor { - void onReevaluationTriggered( - final Event event, - final List revaluableRules, - final CompletionCallback callback); - } - private RuleReevaluationInterceptor reevaluationInterceptor; @VisibleForTesting static final String RULES_ENGINE_NAME = "name"; diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt new file mode 100644 index 000000000..4b3d26aee --- /dev/null +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt @@ -0,0 +1,21 @@ +package com.adobe.marketing.mobile.launch.rulesengine + +import com.adobe.marketing.mobile.Event + +/** A callback that should be invoked when the asynchronous work is complete. */ +interface CompletionCallback { + fun onComplete() +} + +/** + * An interface for an interceptor that is triggered when a [LaunchRule] with the + * re-evaluation flag is triggered. The interceptor is responsible for updating the rules and + * invoking the [CompletionCallback] when complete. + */ +interface RuleReevaluationInterceptor { + fun onReevaluationTriggered( + event: Event?, + revaluableRules: MutableList?, + callback: CompletionCallback? + ) +} \ No newline at end of file diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt index 88ad44055..3926e14c8 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/json/JSONMeta.kt @@ -15,7 +15,20 @@ import com.adobe.marketing.mobile.launch.rulesengine.RuleMeta import org.json.JSONObject /** - * The class representing a Rule's consequence + * Generic utility to parse a meta object from JSON for rules engine. + * + * This class is responsible for extracting meta information from a JSON object. + * Currently, it only parses the `reEvaluate` flag, but it is designed to be extended + * in the future to support additional meta keys as needed. + * + * Example of a meta JSON object: + * ```json + * { + * "reEvaluate": true + * } + * ``` + * + * Future meta keys can be added to this class as requirements evolve. */ internal class JSONMeta private constructor( private val reEvaluate: Boolean, From 855cb00fdf5e032901d86f69ce7c4afb1dae7bdd Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Tue, 10 Feb 2026 01:21:36 +0530 Subject: [PATCH 17/23] Spotless Apply --- .../rulesengine/RuleReevaluationInterceptor.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt index 4b3d26aee..4105584aa 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt @@ -1,3 +1,14 @@ +/* + Copyright 2026 Adobe. All rights reserved. + This file is licensed to you under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. You may obtain a copy + of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under + the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + OF ANY KIND, either express or implied. See the License for the specific language + governing permissions and limitations under the License. +*/ + package com.adobe.marketing.mobile.launch.rulesengine import com.adobe.marketing.mobile.Event @@ -18,4 +29,4 @@ interface RuleReevaluationInterceptor { revaluableRules: MutableList?, callback: CompletionCallback? ) -} \ No newline at end of file +} From 2a6cc1967d83ba7cb12a39958c8e975b0f87b145 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Tue, 10 Feb 2026 16:13:53 +0530 Subject: [PATCH 18/23] Updating callback to AdobeCallback --- .../launch/rulesengine/LaunchRulesEngine.java | 14 ++++++++------ .../rulesengine/RuleReevaluationInterceptor.kt | 10 +++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index 448dea0b8..350ecb30e 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -163,13 +163,15 @@ private Event processAndIntercept(final Event event) { reevaluationInterceptor.onReevaluationTriggered( processedEvent, revaluableRules, - () -> { + (success) -> { // After the interceptor has updated the rules, re-evaluate and process - // consequences - final ArrayList newlyMatchedRules = - new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); - newlyMatchedRules.removeAll(rulesToProcess); - launchRulesConsequence.process(processedEvent, newlyMatchedRules); + // consequences. If update is not success intercepted rules are not processed + if(success) { + final ArrayList newlyMatchedRules = + new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); + newlyMatchedRules.removeAll(rulesToProcess); + launchRulesConsequence.process(processedEvent, newlyMatchedRules); + } }); return processedEvent; } diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt index 4105584aa..7d7ee6a5e 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt @@ -11,22 +11,18 @@ package com.adobe.marketing.mobile.launch.rulesengine +import com.adobe.marketing.mobile.AdobeCallback import com.adobe.marketing.mobile.Event -/** A callback that should be invoked when the asynchronous work is complete. */ -interface CompletionCallback { - fun onComplete() -} - /** * An interface for an interceptor that is triggered when a [LaunchRule] with the * re-evaluation flag is triggered. The interceptor is responsible for updating the rules and - * invoking the [CompletionCallback] when complete. + * invoking the [AdobeCallback] when complete. success is passed as a boolean value. */ interface RuleReevaluationInterceptor { fun onReevaluationTriggered( event: Event?, revaluableRules: MutableList?, - callback: CompletionCallback? + callback: AdobeCallback? ) } From 116f07e47dfe83e6102cd8ee285b8f20d3c1268d Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Tue, 10 Feb 2026 16:20:54 +0530 Subject: [PATCH 19/23] Updating callback to AdobeCallback --- .../RuleReevaluationInterceptor.kt | 2 +- .../LaunchRulesEngineReevaluationTests.kt | 59 ++++++++++--------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt index 7d7ee6a5e..24ca18afc 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor.kt @@ -22,7 +22,7 @@ import com.adobe.marketing.mobile.Event interface RuleReevaluationInterceptor { fun onReevaluationTriggered( event: Event?, - revaluableRules: MutableList?, + revaluableRules: List?, callback: AdobeCallback? ) } diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt index 11551952b..8798126dd 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngineReevaluationTests.kt @@ -11,6 +11,7 @@ package com.adobe.marketing.mobile.launch.rulesengine +import com.adobe.marketing.mobile.AdobeCallback import com.adobe.marketing.mobile.Event import com.adobe.marketing.mobile.EventSource import com.adobe.marketing.mobile.EventType @@ -60,7 +61,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -73,7 +74,7 @@ class LaunchRulesEngineReevaluationTests { val eventCaptor: KArgumentCaptor = argumentCaptor() val rulesCaptor: KArgumentCaptor> = argumentCaptor() - val callbackCaptor: KArgumentCaptor = argumentCaptor() + val callbackCaptor: KArgumentCaptor> = argumentCaptor() verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( eventCaptor.capture(), @@ -95,7 +96,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -118,7 +119,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -141,7 +142,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) // Event with different type that won't match the rule @@ -215,7 +216,7 @@ class LaunchRulesEngineReevaluationTests { launchRulesEngine.replaceRules(reEvaluateRules) launchRulesEngine.addRules(addRule) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -246,7 +247,7 @@ class LaunchRulesEngineReevaluationTests { // Check that initial data is preserved assertEquals("initialValue", processedEvent.eventData?.get("initialKey")) // Check that attached data was added - val attachedData = processedEvent.eventData?.get("attached_data") as? Map + val attachedData = processedEvent.eventData?.get("attached_data") as? Map<*, *> assertEquals("addedValue", attachedData?.get("addedKey")) } @@ -261,7 +262,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -292,7 +293,7 @@ class LaunchRulesEngineReevaluationTests { // Check that initial data is preserved assertEquals("initialValue", processedEvent.eventData?.get("initialKey")) // The attached_data should NOT be present because the rule is held - val attachedData = processedEvent.eventData?.get("attached_data") as? Map + val attachedData = processedEvent.eventData?.get("attached_data") as? Map<*, *> assertEquals(null, attachedData?.get("mixedRuleKey")) } @@ -304,7 +305,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -332,7 +333,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -369,7 +370,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -382,7 +383,7 @@ class LaunchRulesEngineReevaluationTests { launchRulesEngine.processEvent(testEvent) - val callbackCaptor: KArgumentCaptor = argumentCaptor() + val callbackCaptor: KArgumentCaptor> = argumentCaptor() verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( any(), any(), @@ -390,7 +391,7 @@ class LaunchRulesEngineReevaluationTests { ) // Invoke the callback to simulate completion - callbackCaptor.firstValue.onComplete() + callbackCaptor.firstValue.call(true) // Verify that consequence event was dispatched after callback verify(extensionApi, Mockito.atLeastOnce()).dispatch(eventCaptor.capture()) @@ -410,7 +411,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -421,7 +422,7 @@ class LaunchRulesEngineReevaluationTests { launchRulesEngine.processEvent(testEvent) - val callbackCaptor: KArgumentCaptor = argumentCaptor() + val callbackCaptor: KArgumentCaptor> = argumentCaptor() verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( any(), any(), @@ -436,7 +437,7 @@ class LaunchRulesEngineReevaluationTests { launchRulesEngine.addRules(newRules) // Invoke callback - should re-evaluate with new rules - callbackCaptor.firstValue.onComplete() + callbackCaptor.firstValue.call(true) // The new rule (add consequence) should have been evaluated and event modified // We can't directly test this without more complex mocking, but we verified callback executes @@ -451,7 +452,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -493,7 +494,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -525,7 +526,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -556,7 +557,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -589,7 +590,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( @@ -600,7 +601,7 @@ class LaunchRulesEngineReevaluationTests { launchRulesEngine.processEvent(testEvent) - val callbackCaptor: KArgumentCaptor = argumentCaptor() + val callbackCaptor: KArgumentCaptor> = argumentCaptor() verify(mockInterceptor, Mockito.times(1)).onReevaluationTriggered( any(), any(), @@ -610,9 +611,9 @@ class LaunchRulesEngineReevaluationTests { val callback = callbackCaptor.firstValue // Invoke callback multiple times - callback.onComplete() - callback.onComplete() - callback.onComplete() + callback.call(true) + callback.call(true) + callback.call(true) // Should not cause issues - just processes rules multiple times // This is implementation-defined behavior @@ -627,7 +628,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor1 = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor1 = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor1) val testEvent = Event.Builder( @@ -642,7 +643,7 @@ class LaunchRulesEngineReevaluationTests { verify(mockInterceptor1, Mockito.times(1)).onReevaluationTriggered(any(), any(), any()) // Update interceptor - val mockInterceptor2 = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor2 = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor2) launchRulesEngine.processEvent(testEvent) @@ -662,7 +663,7 @@ class LaunchRulesEngineReevaluationTests { assertNotNull(rules) launchRulesEngine.replaceRules(rules) - val mockInterceptor = mock(LaunchRulesEngine.RuleReevaluationInterceptor::class.java) + val mockInterceptor = mock(RuleReevaluationInterceptor::class.java) launchRulesEngine.setRuleReevaluationInterceptor(mockInterceptor) val testEvent = Event.Builder( From 9bd4ce9067fcb5453baa7428c65ccef566848d1a Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Tue, 10 Feb 2026 16:27:47 +0530 Subject: [PATCH 20/23] SpotlessApply --- .../mobile/launch/rulesengine/LaunchRulesEngine.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index 350ecb30e..b31bdddbc 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -165,8 +165,9 @@ private Event processAndIntercept(final Event event) { revaluableRules, (success) -> { // After the interceptor has updated the rules, re-evaluate and process - // consequences. If update is not success intercepted rules are not processed - if(success) { + // consequences. If update is not success intercepted rules are not + // processed + if (success) { final ArrayList newlyMatchedRules = new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); newlyMatchedRules.removeAll(rulesToProcess); From 781bd0130a975d4c916e1bae40582781d277c416 Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Tue, 10 Feb 2026 16:37:23 +0530 Subject: [PATCH 21/23] checkStyle fixes --- .../launch/rulesengine/LaunchRulesEngine.java | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java index b31bdddbc..9b48f039c 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine.java @@ -149,34 +149,34 @@ private Event processAndIntercept(final Event event) { // If no interceptor is set, process consequences immediately. if (reevaluationInterceptor == null) { return launchRulesConsequence.process(event, matchedRules); - } else { - final List revaluableRules = - launchRulesConsequence.getReevaluableRules(matchedRules); - if (revaluableRules.isEmpty()) { - return launchRulesConsequence.process(event, matchedRules); - } else { - final List rulesToHold = - launchRulesConsequence.getRulesToHoldForReevaluation(matchedRules); - final ArrayList rulesToProcess = new ArrayList<>(matchedRules); - rulesToProcess.removeAll(rulesToHold); - Event processedEvent = launchRulesConsequence.process(event, rulesToProcess); - reevaluationInterceptor.onReevaluationTriggered( - processedEvent, - revaluableRules, - (success) -> { - // After the interceptor has updated the rules, re-evaluate and process - // consequences. If update is not success intercepted rules are not - // processed - if (success) { - final ArrayList newlyMatchedRules = - new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); - newlyMatchedRules.removeAll(rulesToProcess); - launchRulesConsequence.process(processedEvent, newlyMatchedRules); - } - }); - return processedEvent; - } } + + final List revaluableRules = + launchRulesConsequence.getReevaluableRules(matchedRules); + if (revaluableRules.isEmpty()) { + return launchRulesConsequence.process(event, matchedRules); + } + + final List rulesToHold = + launchRulesConsequence.getRulesToHoldForReevaluation(matchedRules); + final ArrayList rulesToProcess = new ArrayList<>(matchedRules); + rulesToProcess.removeAll(rulesToHold); + Event processedEvent = launchRulesConsequence.process(event, rulesToProcess); + reevaluationInterceptor.onReevaluationTriggered( + processedEvent, + revaluableRules, + (success) -> { + // After the interceptor has updated the rules, re-evaluate and process + // consequences. If update is not success intercepted rules are not + // processed + if (success) { + final ArrayList newlyMatchedRules = + new ArrayList<>(ruleRulesEngine.evaluate(tokenFinder)); + newlyMatchedRules.removeAll(rulesToProcess); + launchRulesConsequence.process(processedEvent, newlyMatchedRules); + } + }); + return processedEvent; } List getRules() { From 90e280fb51b1fb3a41980ac7697cf4ea0aacfb7e Mon Sep 17 00:00:00 2001 From: sagar-sharma-adobe Date: Tue, 10 Feb 2026 20:01:29 +0530 Subject: [PATCH 22/23] Updating public API --- code/core/api/core.api | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/code/core/api/core.api b/code/core/api/core.api index 1a3cdb45a..cf2e4e08d 100644 --- a/code/core/api/core.api +++ b/code/core/api/core.api @@ -345,15 +345,7 @@ public class com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine { public fun evaluateEvent (Lcom/adobe/marketing/mobile/Event;)Ljava/util/List; public fun processEvent (Lcom/adobe/marketing/mobile/Event;)Lcom/adobe/marketing/mobile/Event; public fun replaceRules (Ljava/util/List;)V - public fun setRuleReevaluationInterceptor (Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine$RuleReevaluationInterceptor;)V -} - -public abstract interface class com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine$CompletionCallback { - public abstract fun onComplete ()V -} - -public abstract interface class com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine$RuleReevaluationInterceptor { - public abstract fun onReevaluationTriggered (Lcom/adobe/marketing/mobile/Event;Ljava/util/List;Lcom/adobe/marketing/mobile/launch/rulesengine/LaunchRulesEngine$CompletionCallback;)V + public fun setRuleReevaluationInterceptor (Lcom/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor;)V } public final class com/adobe/marketing/mobile/launch/rulesengine/RuleConsequence { @@ -386,6 +378,10 @@ public final class com/adobe/marketing/mobile/launch/rulesengine/RuleMeta { public fun toString ()Ljava/lang/String; } +public abstract interface class com/adobe/marketing/mobile/launch/rulesengine/RuleReevaluationInterceptor { + public abstract fun onReevaluationTriggered (Lcom/adobe/marketing/mobile/Event;Ljava/util/List;Lcom/adobe/marketing/mobile/AdobeCallback;)V +} + public class com/adobe/marketing/mobile/launch/rulesengine/download/RulesLoadResult { public fun (Ljava/lang/String;Lcom/adobe/marketing/mobile/launch/rulesengine/download/RulesLoadResult$Reason;)V public fun getData ()Ljava/lang/String; From 69592e04a16236b498e868b507f5c77953b96f91 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Feb 2026 14:38:53 +0000 Subject: [PATCH 23/23] Updating Core version to 3.5.1 --- .../java/com/adobe/marketing/mobile/internal/CoreConstants.kt | 2 +- code/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt index daadaaa07..b8d667721 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt @@ -13,7 +13,7 @@ package com.adobe.marketing.mobile.internal internal object CoreConstants { const val LOG_TAG = "MobileCore" - const val VERSION = "3.6.0" + const val VERSION = "3.5.1" object EventDataKeys { /** diff --git a/code/gradle.properties b/code/gradle.properties index bde0ddad9..883c6a22b 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -5,7 +5,7 @@ android.useAndroidX=true #Maven artifacts #Core extension -coreExtensionVersion=3.6.0 +coreExtensionVersion=3.5.1 coreExtensionName=core coreMavenRepoName=AdobeMobileCoreSdk coreMavenRepoDescription=Android Core Extension for Adobe Mobile Marketing