From 9b06804bb51e346a21920aaa77a815be5c95c517 Mon Sep 17 00:00:00 2001 From: FarhanAnjum-opti Date: Wed, 26 Nov 2025 23:33:18 +0600 Subject: [PATCH] update: Fix reasons and logging in CMAB decision process --- .../main/java/com/optimizely/ab/Optimizely.java | 3 ++- .../optimizely/ab/bucketing/DecisionService.java | 7 ++++--- .../ab/optimizelydecision/OptimizelyDecision.java | 15 ++++++++++++++- .../java/com/optimizely/ab/OptimizelyTest.java | 3 +-- .../com/optimizely/ab/cmab/DefaultCmabClient.java | 1 + 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/Optimizely.java b/core-api/src/main/java/com/optimizely/ab/Optimizely.java index f9631db7c..11d89c03f 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -1494,6 +1494,7 @@ private Map decideForKeysInternal(@Nonnull Optimizel for (int i = 0; i < flagsWithoutForcedDecision.size(); i++) { DecisionResponse decision = decisionList.get(i); boolean error = decision.isError(); + List reasons = decision.getReasons().toReport(); String experimentKey = null; if (decision.getResult() != null && decision.getResult().experiment != null) { experimentKey = decision.getResult().experiment.getKey(); @@ -1501,7 +1502,7 @@ private Map decideForKeysInternal(@Nonnull Optimizel String flagKey = flagsWithoutForcedDecision.get(i).getKey(); if (error) { - OptimizelyDecision optimizelyDecision = OptimizelyDecision.newErrorDecision(flagKey, user, DecisionMessage.CMAB_ERROR.reason(experimentKey)); + OptimizelyDecision optimizelyDecision = OptimizelyDecision.newErrorDecision(flagKey, user, reasons); decisionMap.put(flagKey, optimizelyDecision); if (validKeys.contains(flagKey)) { validKeys.remove(flagKey); diff --git a/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java b/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java index 65703ac55..572d28981 100644 --- a/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java +++ b/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java @@ -957,11 +957,12 @@ private DecisionResponse getDecisionForCmabExperiment(@Nonnull Pro // User is in CMAB allocation, proceed to CMAB decision try { CmabDecision cmabDecision = cmabService.getDecision(projectConfig, userContext, experiment.getId(), options); - + String message = String.format("Successfully fetched CMAB decision %s for experiment %s.", cmabDecision.toString(), experiment.getKey()); + reasons.addInfo(message); return new DecisionResponse<>(cmabDecision, reasons); } catch (Exception e) { - String errorMessage = String.format("CMAB fetch failed for experiment \"%s\"", experiment.getKey()); - reasons.addInfo(errorMessage); + String errorMessage = String.format("Failed to fetch CMAB data for experiment %s.", experiment.getKey()); + reasons.addError(errorMessage); logger.error("{} {}", errorMessage, e.getMessage()); return new DecisionResponse<>(null, reasons, true, null); diff --git a/core-api/src/main/java/com/optimizely/ab/optimizelydecision/OptimizelyDecision.java b/core-api/src/main/java/com/optimizely/ab/optimizelydecision/OptimizelyDecision.java index 1741afbcd..0ccbebfac 100644 --- a/core-api/src/main/java/com/optimizely/ab/optimizelydecision/OptimizelyDecision.java +++ b/core-api/src/main/java/com/optimizely/ab/optimizelydecision/OptimizelyDecision.java @@ -130,7 +130,20 @@ public static OptimizelyDecision newErrorDecision(@Nonnull String key, user, Arrays.asList(error)); } - + + public static OptimizelyDecision newErrorDecision(@Nonnull String key, + @Nonnull OptimizelyUserContext user, + @Nonnull List reasons) { + return new OptimizelyDecision( + null, + false, + new OptimizelyJSON(Collections.emptyMap()), + null, + key, + user, + reasons); + } + @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) return false; diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java index e24de6c2b..db707a7dc 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java @@ -5090,7 +5090,6 @@ public void identifyUser() { @Test public void testDecideReturnsErrorDecisionWhenDecisionServiceFails() throws Exception { assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())); - // Use the CMAB datafile Optimizely optimizely = Optimizely.builder() .withDatafile(validConfigJsonCMAB()) @@ -5099,6 +5098,7 @@ public void testDecideReturnsErrorDecisionWhenDecisionServiceFails() throws Exce // Mock decision service to return an error from CMAB DecisionReasons reasons = new DefaultDecisionReasons(); + reasons.addError("Failed to fetch CMAB data for experiment exp-cmab."); FeatureDecision errorFeatureDecision = new FeatureDecision(new Experiment("123", "exp-cmab", "123"), null, FeatureDecision.DecisionSource.ROLLOUT); DecisionResponse errorDecisionResponse = new DecisionResponse<>( errorFeatureDecision, @@ -5129,7 +5129,6 @@ public void testDecideReturnsErrorDecisionWhenDecisionServiceFails() throws Exce OptimizelyUserContext userContext = optimizely.createUserContext("test_user"); OptimizelyDecision decision = userContext.decide("feature_1"); // This is the feature flag key from cmab-config.json - System.out.println("reasons: " + decision.getReasons()); // Verify the decision contains the error information assertFalse(decision.getEnabled()); assertNull(decision.getVariationKey()); diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/cmab/DefaultCmabClient.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/cmab/DefaultCmabClient.java index 3c549e043..2aaa6b5bb 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/cmab/DefaultCmabClient.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/cmab/DefaultCmabClient.java @@ -101,6 +101,7 @@ private String doFetch(String url, String requestBody) { request.setHeader("content-type", "application/json"); CloseableHttpResponse response = null; try { + logger.info("Fetching CMAB decision: {} with body: {}", url, requestBody); response = httpClient.execute(request); if (!CmabClientHelper.isSuccessStatusCode(response.getStatusLine().getStatusCode())) {