Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement IS_NULL operator and INTEGER value type #8

Merged
merged 11 commits into from
May 29, 2024
8 changes: 7 additions & 1 deletion dot-net-sdk/EppoClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ private EppoClient(ConfigurationStore configurationStore, EppoClientConfig eppoC
}


public int? GetIntegerAssignment(string subjectKey, string flagKey, SubjectAttributes? subjectAttributes = null)
{
return GetAssignment(subjectKey, flagKey, subjectAttributes ?? new SubjectAttributes())?.IntegerValue();
}


public string? GetStringAssignment(string subjectKey, string flagKey, SubjectAttributes? subjectAttributes = null)
{
return GetAssignment(subjectKey, flagKey, subjectAttributes ?? new SubjectAttributes())?.StringValue();
Expand All @@ -59,7 +65,7 @@ private EppoClient(ConfigurationStore configurationStore, EppoClientConfig eppoC
}

var subjectVariationOverride = this.GetSubjectVariationOverride(subjectKey, configuration);
if (!subjectVariationOverride.isNull())
if (!subjectVariationOverride.IsNull())
typotter marked this conversation as resolved.
Show resolved Hide resolved
{
return subjectVariationOverride;
}
Expand Down
59 changes: 27 additions & 32 deletions dot-net-sdk/dto/EppoValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,25 @@ namespace eppo_sdk.dto;
[JsonConverter(typeof(EppoValueDeserializer))]
public class EppoValue
{
public string value { get; set; }
public string? value { get; set; }

public EppoValueType type { get; set; } = EppoValueType.NULL;

public List<string> array { get; set; }
public List<string>? array { get; set; }


public static EppoValue Bool(string? value) => new(value, EppoValueType.BOOLEAN);
public static EppoValue Bool(bool value) => new(value.ToString(), EppoValueType.BOOLEAN);
public static EppoValue Number(string value) => new(value, EppoValueType.NUMBER);
public static EppoValue String(string? value) => new(value, EppoValueType.STRING);
public static EppoValue Integer(string value) => new(value, EppoValueType.INTEGER);
public static EppoValue Null() => new();

public EppoValue()
{
}

public EppoValue(string value, EppoValueType type)
public EppoValue(string? value, EppoValueType type)
{
this.value = value;
this.type = type;
Expand All @@ -28,38 +36,25 @@ public EppoValue(List<string> array)
this.type = EppoValueType.ARRAY_OF_STRING;
}

public EppoValue(EppoValueType type)
{
this.type = type;
}
public EppoValue(EppoValueType type) => this.type = type;

public bool BoolValue()
{
return bool.Parse(value);
}
public bool BoolValue() => bool.Parse(value);

public double DoubleValue()
{
return double.Parse(value, NumberStyles.Number);
}
public double DoubleValue() => double.Parse(value, NumberStyles.Number);

public bool isNumeric()
{
return double.TryParse(value, out _);
}
public int IntegerValue() => int.Parse(value, NumberStyles.Number);

public string StringValue()
{
return value;
}
public bool IsNumeric() => double.TryParse(value, out _);

public List<string> ArrayValue()
{
return array;
}
public string StringValue() => value;

public bool isNull()
{
return EppoValueType.NULL.Equals(type);
}
}
public List<string> ArrayValue() => array;

public bool IsNull() => EppoValueType.NULL.Equals(type);

public static bool IsNullValue(EppoValue? value) =>
value == null /* null pointer */ ||
value.IsNull() /* parsed as a null JSON token type */ ||
value.value == null; /* Value type is set but value is null */

}
11 changes: 6 additions & 5 deletions dot-net-sdk/dto/EppoValueDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ public override void WriteJson(JsonWriter writer, EppoValue? value, JsonSerializ
switch (reader.TokenType)
{
case JsonToken.String:
return new EppoValue(value.ToString(), EppoValueType.STRING);
return EppoValue.String(value.ToString());
case JsonToken.Integer:
return EppoValue.Integer(value.ToString());
case JsonToken.Float:
return new EppoValue(value.ToString(), EppoValueType.NUMBER);
return EppoValue.Number(value.ToString());
case JsonToken.Boolean:
return new EppoValue(value.ToString(), EppoValueType.BOOLEAN);
return EppoValue.Bool(value.ToString());
typotter marked this conversation as resolved.
Show resolved Hide resolved
case JsonToken.Null:
return new EppoValue(EppoValueType.NULL);
return EppoValue.Null();
case JsonToken.StartArray:
var val = new List<string>();
reader.Read();
Expand All @@ -36,7 +37,7 @@ public override void WriteJson(JsonWriter writer, EppoValue? value, JsonSerializ
}
return new EppoValue(val);
default:
throw new UnsupportedEppoValueException("Unsupported Eppo Values");
throw new UnsupportedEppoValueException("Unsupported Eppo Value Type: " + reader.TokenType.ToString());
}
}
}
1 change: 1 addition & 0 deletions dot-net-sdk/dto/EppoValueType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace eppo_sdk.dto;
public enum EppoValueType
{
NUMBER,
INTEGER,
STRING,
BOOLEAN,
NULL,
Expand Down
3 changes: 2 additions & 1 deletion dot-net-sdk/dto/OperatorType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public enum OperatorType
LTE,
LT,
ONE_OF,
NOT_ONE_OF
NOT_ONE_OF,
IS_NULL
}
7 changes: 1 addition & 6 deletions dot-net-sdk/http/ExperimentConfigurationRequester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,15 @@ public ExperimentConfigurationRequester(EppoHttpClient eppoHttpClient) {

public ExperimentConfigurationResponse? FetchExperimentConfiguration()
{
ExperimentConfigurationResponse? config = null;
try
{
return this.eppoHttpClient.Get(Constants.RAC_ENDPOINT);
}
catch (UnauthorizedAccessException e)
{
throw e;
}
catch (Exception e)
{
logger.Warn($"Unable to Fetch Experiment Configuration: {e.Message}");
}

return config;
return null;
typotter marked this conversation as resolved.
Show resolved Hide resolved
}
}
16 changes: 10 additions & 6 deletions dot-net-sdk/validators/RuleValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@ private static bool EvaluateCondition(SubjectAttributes subjectAttributes, Condi
{
try
{
if (subjectAttributes.ContainsKey(condition.attribute) &&
subjectAttributes.TryGetValue(condition.attribute, out EppoValue outVal))
// Operators other than `IS_NULL` need to assume non-null
if (condition.operatorType == IS_NULL) {
bool isNull = !subjectAttributes.TryGetValue(condition.attribute, out EppoValue? outVal) || EppoValue.IsNullValue(outVal);
return condition.value.BoolValue() == isNull;
}
else if (subjectAttributes.TryGetValue(condition.attribute, out EppoValue? outVal))
{
var value = outVal!; // Assuming non-null for simplicity, handle nulls as necessary

if (condition.operatorType == GTE)
{
if (value.isNumeric() && condition.value.isNumeric())
if (value.IsNumeric() && condition.value.IsNumeric())
{
return value.DoubleValue() >= condition.value.DoubleValue();
}
Expand All @@ -50,7 +54,7 @@ private static bool EvaluateCondition(SubjectAttributes subjectAttributes, Condi
}
else if (condition.operatorType == GT)
{
if (value.isNumeric() && condition.value.isNumeric())
if (value.IsNumeric() && condition.value.IsNumeric())
{
return value.DoubleValue() > condition.value.DoubleValue();
}
Expand All @@ -65,7 +69,7 @@ private static bool EvaluateCondition(SubjectAttributes subjectAttributes, Condi
}
else if (condition.operatorType == LTE)
{
if (value.isNumeric() && condition.value.isNumeric())
if (value.IsNumeric() && condition.value.IsNumeric())
{
return value.DoubleValue() <= condition.value.DoubleValue();
}
Expand All @@ -80,7 +84,7 @@ private static bool EvaluateCondition(SubjectAttributes subjectAttributes, Condi
}
else if (condition.operatorType == LT)
{
if (value.isNumeric() && condition.value.isNumeric())
if (value.IsNumeric() && condition.value.IsNumeric())
{
return value.DoubleValue() < condition.value.DoubleValue();
}
Expand Down
18 changes: 18 additions & 0 deletions eppo-sdk-test/EppoClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public void ShouldValidateAssignments(AssignmentTestCase assignmentTestCase)
var numericExpectations = assignmentTestCase.expectedAssignments.ConvertAll(x => x.DoubleValue());
Assert.That(GetNumericAssignments(assignmentTestCase), Is.EqualTo(numericExpectations));

break;
case "integer":
var intExpectations = assignmentTestCase.expectedAssignments.ConvertAll(x => x.IntegerValue());
Assert.That(GetIntegerAssignments(assignmentTestCase), Is.EqualTo(intExpectations));

break;
case "string":
var stringExpectations = assignmentTestCase.expectedAssignments.ConvertAll(x => x.StringValue());
Expand Down Expand Up @@ -99,6 +104,19 @@ public void ShouldValidateAssignments(AssignmentTestCase assignmentTestCase)
client.GetNumericAssignment(subject, assignmentTestCase.experiment));
}

private static List<int?> GetIntegerAssignments(AssignmentTestCase assignmentTestCase)
{
var client = EppoClient.GetInstance();
if (assignmentTestCase.subjectsWithAttributes != null)
{
return assignmentTestCase.subjectsWithAttributes.ConvertAll(subject => client.GetIntegerAssignment(subject.subjectKey, assignmentTestCase.experiment,
subject.subjectAttributes));
}

return assignmentTestCase.subjects.ConvertAll(subject =>
client.GetIntegerAssignment(subject, assignmentTestCase.experiment));
}

private static List<string?> GetStringAssignments(AssignmentTestCase assignmentTestCase)
{
var client = EppoClient.GetInstance();
Expand Down
Loading
Loading