diff --git a/litmus-client/src/main/java/com/navi/medici/client/OkHttpClientContainer.java b/litmus-client/src/main/java/com/navi/medici/client/OkHttpClientContainer.java index 452af94..e37eff0 100644 --- a/litmus-client/src/main/java/com/navi/medici/client/OkHttpClientContainer.java +++ b/litmus-client/src/main/java/com/navi/medici/client/OkHttpClientContainer.java @@ -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(); } } diff --git a/litmus-client/src/main/java/com/navi/medici/event/EventDispatcher.java b/litmus-client/src/main/java/com/navi/medici/event/EventDispatcher.java index 60f0f7e..dfec0b4 100644 --- a/litmus-client/src/main/java/com/navi/medici/event/EventDispatcher.java +++ b/litmus-client/src/main/java/com/navi/medici/event/EventDispatcher.java @@ -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.builder() @@ -57,4 +61,5 @@ public class EventDispatcher { }); } } + } diff --git a/litmus-client/src/main/java/com/navi/medici/litmus/DefaultLitmus.java b/litmus-client/src/main/java/com/navi/medici/litmus/DefaultLitmus.java index 3a5559e..3a6580c 100644 --- a/litmus-client/src/main/java/com/navi/medici/litmus/DefaultLitmus.java +++ b/litmus-client/src/main/java/com/navi/medici/litmus/DefaultLitmus.java @@ -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)); diff --git a/litmus-client/src/main/java/com/navi/medici/strategy/PhoneNumberStrategy.java b/litmus-client/src/main/java/com/navi/medici/strategy/PhoneNumberStrategy.java new file mode 100644 index 0000000..1754471 --- /dev/null +++ b/litmus-client/src/main/java/com/navi/medici/strategy/PhoneNumberStrategy.java @@ -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 parameters) { + return false; + } + + @Override + public boolean isEnabled(Map parameters, LitmusContext litmusContext) { + if (StringUtils.isBlank(parameters.get(PERCENTAGE))) { + return phoneNumberSegmentCheck(parameters, litmusContext); + } else { + return segmentCheckWithRollout(parameters, litmusContext); + } + } + + private boolean phoneNumberSegmentCheck(Map 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 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); + } + + +} diff --git a/litmus-client/src/main/java/com/navi/medici/util/StrategyUtils.java b/litmus-client/src/main/java/com/navi/medici/util/StrategyUtils.java index 634dc36..56b38ad 100644 --- a/litmus-client/src/main/java/com/navi/medici/util/StrategyUtils.java +++ b/litmus-client/src/main/java/com/navi/medici/util/StrategyUtils.java @@ -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 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": diff --git a/litmus-client/src/main/java/com/navi/medici/util/VariantUtil.java b/litmus-client/src/main/java/com/navi/medici/util/VariantUtil.java index 2c05012..899cada 100644 --- a/litmus-client/src/main/java/com/navi/medici/util/VariantUtil.java +++ b/litmus-client/src/main/java/com/navi/medici/util/VariantUtil.java @@ -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())); } diff --git a/litmus-model/src/main/java/com/navi/medici/clickstream/LitmusExperimentEvent.java b/litmus-model/src/main/java/com/navi/medici/clickstream/LitmusExperimentEvent.java index 466304d..b48c550 100644 --- a/litmus-model/src/main/java/com/navi/medici/clickstream/LitmusExperimentEvent.java +++ b/litmus-model/src/main/java/com/navi/medici/clickstream/LitmusExperimentEvent.java @@ -22,4 +22,8 @@ public class LitmusExperimentEvent { Boolean result; String variant; + + String appName; + + String vertical; } diff --git a/litmus-model/src/main/java/com/navi/medici/context/LitmusContext.java b/litmus-model/src/main/java/com/navi/medici/context/LitmusContext.java index 1cbbd62..6bc3f8d 100644 --- a/litmus-model/src/main/java/com/navi/medici/context/LitmusContext.java +++ b/litmus-model/src/main/java/com/navi/medici/context/LitmusContext.java @@ -29,6 +29,8 @@ public class LitmusContext { Optional appVersionCode; Optional osType; Optional deviceId; + + Optional phoneNumber; Map properties; Optional 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 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)); } }