Skip to content

Commit

Permalink
Adding Custom Field Support
Browse files Browse the repository at this point in the history
  • Loading branch information
hermeswaldemarin committed Nov 30, 2023
1 parent dc12a9d commit 83f251a
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 4 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
dotnet-quality: 'preview'

- name: Run Build
run: dotnet build
21 changes: 21 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Publish Nuget
on:
push:
branches:
- "main"
tags:
- v*
jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
dotnet-quality: 'preview'

- name: Run Build
run: dotnet build
1 change: 0 additions & 1 deletion src/ABSmartly.Sdk/ABSmartly.Sdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

<ItemGroup>
<Folder Include="JsonExpressions\Operators\" />
<Folder Include="Models\" />
</ItemGroup>

<ItemGroup>
Expand Down
126 changes: 126 additions & 0 deletions src/ABSmartly.Sdk/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class Context : IContext, IDisposable, IAsyncDisposable

private bool _failed;
private Dictionary<string, ExperimentVariables> _index;
private Dictionary<string, Dictionary<string, ContextCustomFieldValue>> _contextCustomFields;

private DictionaryLockableAdapter<string, ExperimentVariables> _indexVariables;

private volatile int _pendingCount;
Expand Down Expand Up @@ -460,6 +462,84 @@ private ExperimentVariables GetExperiment(string experimentName)
_dataLock.ExitReadLock();
}
}

public List<String> GetCustomFieldKeys()
{
try
{
_dataLock.EnterReadLock();

var keys = new List<String>();

foreach (var experiment in _data.Experiments)
{
var customFieldValues = experiment.CustomFieldValues;
if (customFieldValues != null)
{
foreach (var customFieldValue in customFieldValues)
{
keys.Add(customFieldValue.Name);
}
}
}

return keys.OrderBy(q => q).Distinct().ToList();
}
finally
{
_dataLock.ExitReadLock();
}
}

public Object GetCustomFieldValue(String environmentName, String key)
{
try
{
_dataLock.EnterReadLock();

_contextCustomFields.TryGetValue(environmentName, out var customFieldValues);

if (customFieldValues != null)
{
customFieldValues.TryGetValue(key, out var field);
if (field != null)
{
return field.Value;
}
}

return null;
}
finally
{
_dataLock.ExitReadLock();
}
}

public Object GetCustomFieldType(String environmentName, String key)
{
try
{
_dataLock.EnterReadLock();

_contextCustomFields.TryGetValue(environmentName, out var customFieldValues);

if (customFieldValues != null)
{
customFieldValues.TryGetValue(key, out var field);
if (field != null)
{
return field.Type;
}
}

return null;
}
finally
{
_dataLock.ExitReadLock();
}
}

private ExperimentVariables GetVariableExperiment(string key)
{
Expand Down Expand Up @@ -854,6 +934,7 @@ private void SetData(ContextData data)
{
var index = new Dictionary<string, ExperimentVariables>();
var indexVariables = new Dictionary<string, ExperimentVariables>();
var contextCustomFields = new Dictionary<string, Dictionary<string, ContextCustomFieldValue>>();

foreach (var experiment in data.Experiments)
{
Expand All @@ -878,13 +959,51 @@ private void SetData(ContextData data)
}

index[experiment.Name] = experimentVariables;

if (experiment.CustomFieldValues == null) continue;

var experimentCustomFields = new Dictionary<string, ContextCustomFieldValue>();
foreach (var customFieldValue in experiment.CustomFieldValues)
{
var value = new ContextCustomFieldValue
{
Type = customFieldValue.Type
};

if (customFieldValue.Value != null)
{
var customValue = customFieldValue.Value;

if (customFieldValue.Type.StartsWith("json"))
{
value.Value = _variableParser.Parse(this, experiment.Name, customFieldValue.Name, customValue);
}
else if(customFieldValue.Type.StartsWith("boolean"))
{
value.Value = Convert.ToBoolean(customValue);
}
else if(customFieldValue.Type.StartsWith("number"))
{
value.Value = Convert.ToInt64(customValue);
}
else
{
value.Value = customValue;
}
}

experimentCustomFields[customFieldValue.Name] = value;
}

contextCustomFields[experiment.Name] = experimentCustomFields;
}

try
{
_dataLock.EnterWriteLock();

_index = index;
_contextCustomFields = contextCustomFields;
_indexVariables =
new DictionaryLockableAdapter<string, ExperimentVariables>(new LockableCollectionSlimLock(_dataLock),
indexVariables);
Expand Down Expand Up @@ -998,6 +1117,13 @@ public class ExperimentVariables
public Experiment Data { get; set; }
public List<Dictionary<string, object>> Variables { get; set; }
}

public class ContextCustomFieldValue
{
public String Name { get; set; }
public String Type { get; set; }
public Object Value { get; set; }
}

public class Assignment
{
Expand Down
46 changes: 46 additions & 0 deletions src/ABSmartly.Sdk/Models/CustomFieldValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Diagnostics;

namespace ABSmartly.Models;

[DebuggerDisplay("{DebugView},nq")]
public class CustomFieldValue
{
public string Name { get; set; }
public string Type { get; set; }
public string Value { get; set; }

private string DebugView => $"ExperimentVariant{{name={Name}, type={Type}, value={Value}}}";

public override string ToString()
{
return DebugView;
}


#region Equality members

protected bool Equals(CustomFieldValue other)
{
return Name == other.Name &&
Type == other.Type&&
Value == other.Value;
}

public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((CustomFieldValue)obj);
}

public override int GetHashCode()
{
unchecked
{
return ((Name?.GetHashCode() ?? 0) * 397) ^ (Type?.GetHashCode() ?? 0) ^ (Value?.GetHashCode() ?? 0);
}
}

#endregion
}
1 change: 1 addition & 0 deletions src/ABSmartly.Sdk/Models/Experiment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class Experiment
public int FullOnVariant { get; set; }
public ExperimentApplication[] Applications { get; set; }
public ExperimentVariant[] Variants { get; set; }
public CustomFieldValue[] CustomFieldValues { get; set; }
public bool AudienceStrict { get; set; }
public string Audience { get; set; }

Expand Down
45 changes: 45 additions & 0 deletions tests/ABSmartly.Sdk.Tests/ContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,51 @@ public void TestGetVariableKeys()

context.GetVariableKeys().Should().BeEquivalentTo(_variableExperiments);
}

[Test]
public void TestGetCustomFieldKeys()
{
var context = CreateContext(_data);

context.GetCustomFieldKeys().Should().BeEquivalentTo(new List<String> { "country", "languages", "overrides" });
}

[Test]
public void TestGetCustomFieldValues()
{
var context = CreateContext(_data);

context.GetCustomFieldValue("not_found", "not_found").Should().BeNull();
context.GetCustomFieldValue("exp_test_ab", "not_found").Should().BeNull();
context.GetCustomFieldValue("exp_test_ab", "country").Should().BeEquivalentTo("US,PT,ES,DE,FR");
context.GetCustomFieldType("exp_test_ab", "country").Should().BeEquivalentTo("string");

context.GetCustomFieldValue("exp_test_ab", "overrides").Should().BeEquivalentTo(new Dictionary<String, Object>
{
{ "123", 1 },
{ "456", 0 }
}
);
context.GetCustomFieldType("exp_test_ab", "overrides").Should().BeEquivalentTo("json");

context.GetCustomFieldValue("exp_test_ab", "languages").Should().BeNull();
context.GetCustomFieldValue("exp_test_ab", "languages").Should().BeNull();

context.GetCustomFieldValue("exp_test_abc", "overrides").Should().BeNull();
context.GetCustomFieldValue("exp_test_abc", "overrides").Should().BeNull();

context.GetCustomFieldValue("exp_test_abc", "languages").Should().BeEquivalentTo("en-US,en-GB,pt-PT,pt-BR,es-ES,es-MX");
context.GetCustomFieldType("exp_test_abc", "languages").Should().BeEquivalentTo("string");

context.GetCustomFieldValue("exp_test_no_custom_fields", "country").Should().BeNull();
context.GetCustomFieldValue("exp_test_no_custom_fields", "country").Should().BeNull();

context.GetCustomFieldValue("exp_test_no_custom_fields", "overrides").Should().BeNull();
context.GetCustomFieldValue("exp_test_no_custom_fields", "overrides").Should().BeNull();

context.GetCustomFieldValue("exp_test_no_custom_fields", "languages").Should().BeNull();
context.GetCustomFieldValue("exp_test_no_custom_fields", "languages").Should().BeNull();
}

[Test]
public void TestPeekTreatmentReturnsOverrideVariant()
Expand Down
31 changes: 28 additions & 3 deletions tests/ABSmartly.Sdk.Tests/Resources/context.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,19 @@
"config":"{\"banner.border\":1,\"banner.size\":\"large\"}"
}
],
"audience": null
"audience": null,
"customFieldValues": [
{
"name": "country",
"value": "US,PT,ES,DE,FR",
"type": "string"
},
{
"name": "overrides",
"value": "{\"123\":1,\"456\":0}",
"type": "json"
}
]
},
{
"id":2,
Expand Down Expand Up @@ -73,7 +85,19 @@
"config":"{\"button.color\":\"red\"}"
}
],
"audience": ""
"audience": "",
"customFieldValues": [
{
"name": "country",
"value": "US,PT,ES,DE,FR",
"type": "string"
},
{
"name": "languages",
"value": "en-US,en-GB,pt-PT,pt-BR,es-ES,es-MX",
"type": "string"
}
]
},
{
"id":3,
Expand Down Expand Up @@ -113,7 +137,8 @@
"config":"{\"card.width\":\"75%\"}"
}
],
"audience": "{}"
"audience": "{}",
"customFieldValues": null
},
{
"id":4,
Expand Down

0 comments on commit 83f251a

Please sign in to comment.