Introducing phonenumber stickiness, phone number segment strategy

This commit is contained in:
chandresh pancholi
2022-07-20 23:58:51 +05:30
parent e5758eb294
commit 41bfc5f0f0
8 changed files with 101 additions and 16 deletions

View File

@@ -1,6 +1,7 @@
package com.navi.medici.client;
import java.util.concurrent.TimeUnit;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
public class OkHttpClientContainer {
@@ -18,6 +19,7 @@ public class OkHttpClientContainer {
.readTimeout(1, TimeUnit.SECONDS)
.callTimeout(1, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(20, 1, TimeUnit.MINUTES))
.build();
}
}

View File

@@ -17,10 +17,12 @@ import lombok.extern.log4j.Log4j2;
@Log4j2
public class EventDispatcher {
private final ClickStreamClient clickStreamClient;
private final LitmusConfig litmusConfig;
public EventDispatcher(LitmusConfig litmusConfig) {
this.clickStreamClient = new ClickStreamClient(litmusConfig.getClickStreamAPI(), litmusConfig.getAppName(),
litmusConfig.getVertical(), litmusConfig.getMeterRegistry());
this.litmusConfig = litmusConfig;
}
public void publish(LitmusContext litmusContext, LitmusExperiment litmusExperiment, Boolean result, Variant variant) {
@@ -37,6 +39,8 @@ public class EventDispatcher {
.experimentName(litmusExperiment.getName())
.variant(variantName)
.result(result)
.appName(litmusConfig.getAppName())
.vertical(litmusConfig.getVertical())
.build();
var event = ClickStreamEvent.<LitmusExperimentEvent>builder()
@@ -57,4 +61,5 @@ public class EventDispatcher {
});
}
}
}

View File

@@ -17,6 +17,7 @@ import com.navi.medici.request.v1.LitmusExperiment;
import com.navi.medici.strategy.DefaultStrategy;
import com.navi.medici.strategy.DeviceWithIdStrategy;
import com.navi.medici.strategy.FlexibleRolloutStrategy;
import com.navi.medici.strategy.PhoneNumberStrategy;
import com.navi.medici.strategy.Strategy;
import com.navi.medici.strategy.UnknownStrategy;
import com.navi.medici.strategy.UserWithIdStrategy;
@@ -35,8 +36,6 @@ import lombok.extern.log4j.Log4j2;
@Log4j2
public class DefaultLitmus implements Litmus {
public static final Variant DISABLED_VARIANT = new Variant("disabled", null, false, null);
@@ -178,6 +177,9 @@ public class DefaultLitmus implements Litmus {
new DeviceWithIdStrategy(new HttpExperimentFetcher(litmusConfig.getLitmusURLs(), litmusConfig.getAppName(), litmusConfig.getNamePrefix(),
litmusConfig.getProjectName(), litmusConfig.getVertical(), litmusConfig.getMeterRegistry())),
new PhoneNumberStrategy(new HttpExperimentFetcher(litmusConfig.getLitmusURLs(), litmusConfig.getAppName(), litmusConfig.getNamePrefix(),
litmusConfig.getProjectName(), litmusConfig.getVertical(), litmusConfig.getMeterRegistry())),
new FlexibleRolloutStrategy());
BUILTIN_STRATEGIES.forEach(strategy -> map.put(strategy.getName(), strategy));

View File

@@ -0,0 +1,64 @@
package com.navi.medici.strategy;
import static com.navi.medici.strategy.FlexibleRolloutStrategy.GROUP_ID;
import static com.navi.medici.strategy.FlexibleRolloutStrategy.PERCENTAGE;
import com.navi.medici.client.HttpExperimentFetcher;
import com.navi.medici.context.LitmusContext;
import com.navi.medici.util.StrategyUtils;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
public class PhoneNumberStrategy implements Strategy {
private static final String SEGMENT = "segment";
private static final String STRATEGY_NAME = "phoneNumber";
private final HttpExperimentFetcher experimentFetcher;
public PhoneNumberStrategy(HttpExperimentFetcher experimentFetcher) {
this.experimentFetcher = experimentFetcher;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
@Override
public boolean isEnabled(Map<String, String> parameters) {
return false;
}
@Override
public boolean isEnabled(Map<String, String> parameters, LitmusContext litmusContext) {
if (StringUtils.isBlank(parameters.get(PERCENTAGE))) {
return phoneNumberSegmentCheck(parameters, litmusContext);
} else {
return segmentCheckWithRollout(parameters, litmusContext);
}
}
private boolean phoneNumberSegmentCheck(Map<String, String> parameters, LitmusContext litmusContext) {
var userId = litmusContext.getUserId().orElse(null);
var segmentName = parameters.get(SEGMENT);
var result = experimentFetcher.segmentIdExists(segmentName, userId);
return result.getData() != null && (Boolean) result.getData();
}
private boolean segmentCheckWithRollout(Map<String, String> parameters, LitmusContext litmusContext) {
var userId = litmusContext.getUserId().orElse(null);
var percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE));
var groupId = parameters.getOrDefault(GROUP_ID, "");
var norm = StrategyUtils.getNormalizedNumber(userId, groupId);
return Optional.of(phoneNumberSegmentCheck(parameters, litmusContext))
.map(r -> r && percentage > 0 && norm <= percentage)
.orElse(false);
}
}

View File

@@ -43,11 +43,9 @@ public class StrategyUtils {
public static int getPercentage(String percentage) {
if (isNotEmpty(percentage) && isNumeric(percentage)) {
int p = Integer.parseInt(percentage);
return p;
} else {
return 0;
return Integer.parseInt(percentage);
}
return 0;
}
public static Optional<String> resolveStickiness(String stickiness, LitmusContext context) {
@@ -59,6 +57,8 @@ public class StrategyUtils {
return context.getSessionId();
case "deviceId":
return context.getDeviceId();
case "phoneNumber":
return context.getPhoneNumber();
case "appVersionCode":
return context.getAppVersionCode();
case "osType":

View File

@@ -38,6 +38,9 @@ public final class VariantUtil {
case "deviceId":
contextValue = context.getDeviceId();
break;
case "phoneNumber":
contextValue = context.getPhoneNumber();
break;
default:
contextValue = Optional.ofNullable(context.getProperties().get(override.getContextName()));
}

View File

@@ -22,4 +22,8 @@ public class LitmusExperimentEvent {
Boolean result;
String variant;
String appName;
String vertical;
}

View File

@@ -29,6 +29,8 @@ public class LitmusContext {
Optional<String> appVersionCode;
Optional<String> osType;
Optional<String> deviceId;
Optional<String> phoneNumber;
Map<String, String> properties;
Optional<String> clickStreamPayload;
@@ -51,6 +53,8 @@ public class LitmusContext {
return osType;
case "deviceId":
return deviceId;
case "phoneNumber":
return phoneNumber;
default:
return Optional.ofNullable(properties.get(contextName));
}
@@ -62,23 +66,17 @@ public class LitmusContext {
public static class Builder {
@Nullable
private String appName;
@Nullable
private String environment;
@Nullable
private String userId;
@Nullable
private String sessionId;
@Nullable
private String remoteAddress;
@Nullable
private String appVersionCode;
@Nullable
private String osType;
@Nullable
private String deviceId;
@Nullable
private String phoneNumber;
private String clickStreamPayload;
private final Map<String, String> properties = new HashMap<>();
@@ -95,6 +93,7 @@ public class LitmusContext {
context.appVersionCode.ifPresent(val -> this.appVersionCode = val);
context.osType.ifPresent(val -> this.osType = val);
context.deviceId.ifPresent(val -> this.deviceId = val);
context.phoneNumber.ifPresent(val -> this.phoneNumber = val);
context.clickStreamPayload.ifPresent(val -> this.clickStreamPayload = val);
this.properties.putAll(context.properties);
}
@@ -139,6 +138,11 @@ public class LitmusContext {
return this;
}
public Builder phoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
return this;
}
public Builder addProperty(String name, String value) {
properties.put(name, value);
return this;
@@ -153,7 +157,8 @@ public class LitmusContext {
return new LitmusContext(
Optional.ofNullable(environment), Optional.ofNullable(appName), Optional.ofNullable(userId),
Optional.ofNullable(sessionId), Optional.ofNullable(remoteAddress), Optional.ofNullable(appVersionCode),
Optional.ofNullable(osType), Optional.ofNullable(deviceId), properties, Optional.ofNullable(clickStreamPayload));
Optional.ofNullable(osType), Optional.ofNullable(deviceId), Optional.ofNullable(phoneNumber), properties,
Optional.ofNullable(clickStreamPayload));
}
}