Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run-test-harness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ jobs:
sdks-to-test: java
sdk-github-sha: ${{github.event.pull_request.head.sha}}
github-token: ${{ secrets.TEST_HARNESS_GH_SECRET }}

sdk-capabilities: '["cloud", "edgeDB", "clientCustomData", "v2Config", "allFeatures", "allVariables", "evalReason", "eventsEvalReason", "cloudEvalReason"]'
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

def wasmResourcePath = "$projectDir/src/main/resources"
def wasmVersion = "1.35.1"
def wasmVersion = "1.41.0"
def wasmUrl = "https://unpkg.com/@devcycle/bucketing-assembly-script@$wasmVersion/build/bucketing-lib.release.wasm"
task downloadDVCBucketingWASM(type: Download) {
src wasmUrl
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package com.devcycle.sdk.server.cloud.api;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import com.devcycle.sdk.server.cloud.model.DevCycleCloudOptions;
import com.devcycle.sdk.server.common.api.IDevCycleApi;
import com.devcycle.sdk.server.common.api.IDevCycleClient;
Expand All @@ -8,19 +15,29 @@
import com.devcycle.sdk.server.common.exception.BeforeHookError;
import com.devcycle.sdk.server.common.exception.DevCycleException;
import com.devcycle.sdk.server.common.logging.DevCycleLogger;
import com.devcycle.sdk.server.common.model.*;
import com.devcycle.sdk.server.common.model.BaseVariable;
import com.devcycle.sdk.server.common.model.DevCycleEvent;
import com.devcycle.sdk.server.common.model.DevCycleResponse;
import com.devcycle.sdk.server.common.model.DevCycleUser;
import com.devcycle.sdk.server.common.model.DevCycleUserAndEvents;
import com.devcycle.sdk.server.common.model.ErrorResponse;
import com.devcycle.sdk.server.common.model.EvalHook;
import com.devcycle.sdk.server.common.model.EvalHooksRunner;
import com.devcycle.sdk.server.common.model.EvalReason;
import com.devcycle.sdk.server.common.model.Feature;
import com.devcycle.sdk.server.common.model.HookContext;
import com.devcycle.sdk.server.common.model.HttpResponseCode;
import com.devcycle.sdk.server.common.model.Variable;
import com.devcycle.sdk.server.common.model.Variable.TypeEnum;
import com.devcycle.sdk.server.openfeature.DevCycleProvider;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;

import dev.openfeature.sdk.FeatureProvider;
import retrofit2.Call;
import retrofit2.Response;

import java.io.IOException;
import java.util.*;

public final class DevCycleCloudClient implements IDevCycleClient {

private static final ObjectMapper OBJECT_MAPPER = ObjectMapperUtils.createDefaultObjectMapper();
Expand Down Expand Up @@ -134,8 +151,8 @@ public <T> Variable<T> variable(DevCycleUser user, String key, T defaultValue) {
throw beforeError;
}

evalHooksRunner.executeAfter(reversedHooks, context, variable);
variable.setIsDefaulted(false);
evalHooksRunner.executeAfter(reversedHooks, context, variable);
} catch (Throwable exception) {
if (!(exception instanceof BeforeHookError || exception instanceof AfterHookError)) {
variable = (Variable<T>) Variable.builder()
Expand All @@ -145,6 +162,12 @@ public <T> Variable<T> variable(DevCycleUser user, String key, T defaultValue) {
.defaultValue(defaultValue)
.isDefaulted(true)
.build();

if (exception.getMessage().equals("Variable type mismatch, returning default value")) {
Comment thread
kaushalkapasi marked this conversation as resolved.
Outdated
variable.setEval(EvalReason.defaultReason(EvalReason.DefaultReasonDetailsEnum.VARIABLE_TYPE_MISMATCH));
} else {
variable.setEval(EvalReason.defaultReason(EvalReason.DefaultReasonDetailsEnum.ERROR));
}
}

evalHooksRunner.executeError(reversedHooks, context, exception);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.devcycle.sdk.server.common.model.Variable.TypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -27,4 +28,7 @@ public class BaseVariable {

@Schema(required = true, description = "Variable value can be a string, number, boolean, or JSON")
private Object value;

@Schema(description = "Evaluation reason")
private EvalReason eval;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.devcycle.sdk.server.common.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;

@Data
@AllArgsConstructor
@RequiredArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
Comment thread
kaushalkapasi marked this conversation as resolved.
public class EvalReason {
@Schema(description = "Evaluation reason", required = true)
@JsonProperty("reason")
private String reason;

@Schema(description = "Details")
@JsonProperty("details")
private String details;

@Schema(description = "Target ID")
@JsonProperty("target_id")
@JsonInclude(value=JsonInclude.Include.NON_EMPTY, content=JsonInclude.Include.NON_NULL)
private String targetId;

private EvalReason(String reason, String details) {
this.reason = reason;
this.details = details;
}

public static EvalReason defaultReason(DefaultReasonDetailsEnum details) {
return new EvalReason("DEFAULT", details.getValue());
}

public String getReason() {
return reason == null ? "UNKNOWN" : reason;
}

public enum DefaultReasonDetailsEnum {
MISSING_CONFIG("Missing Config"),
USER_NOT_TARGETED("User Not Targeted"),
VARIABLE_TYPE_MISMATCH("Variable Type Mismatch"),
ERROR("Error");

private final String value;

DefaultReasonDetailsEnum(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package com.devcycle.sdk.server.common.model;

import java.util.HashMap;
import java.util.LinkedHashMap;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonValue;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashMap;
import java.util.LinkedHashMap;

@Data
@Builder
@AllArgsConstructor
Expand All @@ -32,6 +33,13 @@ public class Variable<T> {
@Builder.Default
private Boolean isDefaulted = false;

@Builder.Default
@Deprecated()
private String evalReason = null;

@Schema(description = "Evaluation reason")
private EvalReason eval;

public enum TypeEnum {
STRING("String"),
BOOLEAN("Boolean"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
package com.devcycle.sdk.server.local.api;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import com.devcycle.sdk.server.common.api.IDevCycleClient;
import com.devcycle.sdk.server.common.exception.BeforeHookError;
import com.devcycle.sdk.server.common.logging.DevCycleLogger;
import com.devcycle.sdk.server.common.model.*;
import com.devcycle.sdk.server.common.model.BaseVariable;
import com.devcycle.sdk.server.common.model.DevCycleEvent;
import com.devcycle.sdk.server.common.model.DevCycleUser;
import com.devcycle.sdk.server.common.model.EvalHook;
import com.devcycle.sdk.server.common.model.EvalHooksRunner;
import com.devcycle.sdk.server.common.model.EvalReason;
import com.devcycle.sdk.server.common.model.Feature;
import com.devcycle.sdk.server.common.model.HookContext;
import com.devcycle.sdk.server.common.model.PlatformData;
import com.devcycle.sdk.server.common.model.Variable;
import com.devcycle.sdk.server.common.model.Variable.TypeEnum;
import com.devcycle.sdk.server.local.bucketing.LocalBucketing;
import com.devcycle.sdk.server.local.managers.EnvironmentConfigManager;
Expand All @@ -18,9 +33,8 @@
import com.devcycle.sdk.server.openfeature.DevCycleProvider;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfeature.sdk.FeatureProvider;

import java.util.*;
import dev.openfeature.sdk.FeatureProvider;

public final class DevCycleLocalClient implements IDevCycleClient {

Expand Down Expand Up @@ -141,8 +155,14 @@ public <T> Variable<T> variable(DevCycleUser user, String key, T defaultValue) {

if (!isInitialized()) {
DevCycleLogger.info("Variable called before DevCycleLocalClient has initialized, returning default value");
defaultVariable.setEval(EvalReason.defaultReason(EvalReason.DefaultReasonDetailsEnum.MISSING_CONFIG));
try {
eventQueueManager.queueAggregateEvent(DevCycleEvent.builder().type("aggVariableDefaulted").target(key).build(), null);
eventQueueManager.queueAggregateEvent(DevCycleEvent.builder()
.type("aggVariableDefaulted")
.target(key)
.metaData(Map.of("evalReason", defaultVariable.getEval().getReason()))
.build(),
null);
} catch (Exception e) {
DevCycleLogger.error("Unable to parse aggVariableDefaulted event for Variable " + key + " due to error: " + e, e);
}
Expand Down Expand Up @@ -177,11 +197,13 @@ public <T> Variable<T> variable(DevCycleUser user, String key, T defaultValue) {

if (variableData == null || variableData.length == 0) {
variable = defaultVariable;
variable.setEval(EvalReason.defaultReason(EvalReason.DefaultReasonDetailsEnum.USER_NOT_TARGETED));
} else {
SDKVariable_PB sdkVariable = SDKVariable_PB.parseFrom(variableData);
if (sdkVariable.getType() != pbVariableType) {
DevCycleLogger.warning("Variable type mismatch, returning default value");
variable = defaultVariable;
variable.setEval(EvalReason.defaultReason(EvalReason.DefaultReasonDetailsEnum.VARIABLE_TYPE_MISMATCH));
} else {
variable = ProtobufUtils.createVariable(sdkVariable, defaultValue);
}
Expand All @@ -200,6 +222,7 @@ public <T> Variable<T> variable(DevCycleUser user, String key, T defaultValue) {
} finally {
if (variable == null) {
variable = defaultVariable;
variable.setEval(EvalReason.defaultReason(EvalReason.DefaultReasonDetailsEnum.USER_NOT_TARGETED));
}
evalHooksRunner.executeFinally(reversedHooks, hookContext, Optional.of(variable));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package com.devcycle.sdk.server.local.managers;

import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.devcycle.sdk.server.common.api.IDevCycleApi;
import com.devcycle.sdk.server.common.logging.DevCycleLogger;
import com.devcycle.sdk.server.common.model.DevCycleEvent;
Expand All @@ -13,15 +19,10 @@
import com.devcycle.sdk.server.local.model.FlushPayload;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;

import retrofit2.Call;
import retrofit2.Response;

import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class EventQueueManager {

private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
Expand Down Expand Up @@ -115,10 +116,13 @@ public void queueAggregateEvent(DevCycleEvent event, BucketedUserConfig bucketed
return;
}

String eventString = OBJECT_MAPPER.writeValueAsString(event);

if (bucketedConfig != null) {
this.localBucketing.queueAggregateEvent(this.sdkKey, OBJECT_MAPPER.writeValueAsString(event), OBJECT_MAPPER.writeValueAsString(bucketedConfig.variableVariationMap));
String variableVariationMapString = OBJECT_MAPPER.writeValueAsString(bucketedConfig.variableVariationMap);
this.localBucketing.queueAggregateEvent(this.sdkKey, eventString, variableVariationMapString);
} else {
this.localBucketing.queueAggregateEvent(this.sdkKey, OBJECT_MAPPER.writeValueAsString(event), "{}");
this.localBucketing.queueAggregateEvent(this.sdkKey, eventString, "{}");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
package com.devcycle.sdk.server.local.utils;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import com.devcycle.sdk.server.common.model.DevCycleUser;
import com.devcycle.sdk.server.common.model.EvalReason;
import com.devcycle.sdk.server.common.model.Variable;
import com.devcycle.sdk.server.local.protobuf.*;
import com.devcycle.sdk.server.local.protobuf.CustomDataType;
import com.devcycle.sdk.server.local.protobuf.CustomDataValue;
import com.devcycle.sdk.server.local.protobuf.DVCUser_PB;
import com.devcycle.sdk.server.local.protobuf.EvalReason_PB;
import com.devcycle.sdk.server.local.protobuf.NullableCustomData;
import com.devcycle.sdk.server.local.protobuf.NullableDouble;
import com.devcycle.sdk.server.local.protobuf.NullableString;
import com.devcycle.sdk.server.local.protobuf.SDKVariable_PB;
import com.devcycle.sdk.server.local.protobuf.VariableType_PB;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class ProtobufUtils {
public static DVCUser_PB createDVCUserPB(DevCycleUser user) {
double appBuild = Double.NaN;
Expand Down Expand Up @@ -50,6 +59,7 @@ public static <T> Variable<T> createVariable(SDKVariable_PB sdkVariable, T defau
.value(sdkVariable.getBoolValue())
.defaultValue(defaultValue)
.isDefaulted(false)
.eval(convertToEvalReason(sdkVariable.getEval()))
.build();
break;
case String:
Expand All @@ -59,6 +69,7 @@ public static <T> Variable<T> createVariable(SDKVariable_PB sdkVariable, T defau
.value(sdkVariable.getStringValue())
.defaultValue(defaultValue)
.isDefaulted(false)
.eval(convertToEvalReason(sdkVariable.getEval()))
.build();
break;
case Number:
Expand All @@ -68,6 +79,7 @@ public static <T> Variable<T> createVariable(SDKVariable_PB sdkVariable, T defau
.value(sdkVariable.getDoubleValue())
.defaultValue(defaultValue)
.isDefaulted(false)
.eval(convertToEvalReason(sdkVariable.getEval()))
.build();
break;
case JSON:
Expand All @@ -80,6 +92,7 @@ public static <T> Variable<T> createVariable(SDKVariable_PB sdkVariable, T defau
.value(jsonObject)
.defaultValue(defaultValue)
.isDefaulted(false)
.eval(convertToEvalReason(sdkVariable.getEval()))
.build();
break;
default:
Expand Down Expand Up @@ -143,5 +156,8 @@ public static VariableType_PB convertTypeEnumToVariableType(Variable.TypeEnum ty
}
}

public static EvalReason convertToEvalReason(EvalReason_PB eval) {
return new EvalReason(eval.getReason(), eval.getDetails(), eval.getTargetId());
}

}
Loading
Loading