Skip to content

Commit 387a010

Browse files
authored
feat: dotnet SDK Relay server (#76)
1 parent a1d66c1 commit 387a010

23 files changed

+614
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
bin
2+
obj
3+
tmp
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": []
4+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using eppo_sdk.dto;
2+
using eppo_sdk.dto.bandit;
3+
using eppo_sdk.logger;
4+
5+
internal class AssignmentLogger : IAssignmentLogger
6+
{
7+
public void LogAssignment(AssignmentLogData assignmentLogData)
8+
{
9+
Console.WriteLine("Assignment Log");
10+
Console.WriteLine(assignmentLogData);
11+
}
12+
13+
public void LogBanditAction(BanditLogEvent banditLogEvent)
14+
{
15+
Console.WriteLine("Bandit Action Log");
16+
Console.WriteLine(banditLogEvent);
17+
}
18+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
3+
using EppoSDKRelay.DTO;
4+
using eppo_sdk;
5+
using System.Text.Json;
6+
using Newtonsoft.Json.Linq;
7+
using EppoSDKRelay.util;
8+
9+
namespace EppoSDKRelay.controllers;
10+
11+
[Route("flags/v1/assignment")]
12+
public class AssignmentController : JsonControllerBase
13+
{
14+
[HttpPost]
15+
public ActionResult<string> Post([FromBody] AssignmentRequest data)
16+
{
17+
// If there was a parsing error, this is how it is recorded. We want to fail this test so return an error
18+
var errors = ModelState.Values.SelectMany(x => x.Errors);
19+
foreach (var err in errors)
20+
{
21+
return JsonError(err.ErrorMessage);
22+
}
23+
24+
var eppoClient = EppoClient.GetInstance();
25+
26+
var defaultValue = data.DefaultValue ?? "";
27+
Dictionary<string, object> convertedAttributes = Values.ConvertJsonValuesToPrimitives(data.SubjectAttributes);
28+
29+
switch (data.AssignmentType)
30+
{
31+
case "STRING":
32+
return JsonResult(eppoClient.GetStringAssignment(data.Flag, data.SubjectKey, convertedAttributes, defaultValue.ToString()!));
33+
34+
case "INTEGER":
35+
var intResults = eppoClient.GetIntegerAssignment(data.Flag, data.SubjectKey, convertedAttributes, Convert.ToInt64(defaultValue.ToString()));
36+
return JsonResult(intResults);
37+
38+
case "BOOLEAN":
39+
return JsonResult(eppoClient.GetBooleanAssignment(data.Flag, data.SubjectKey, convertedAttributes, Convert.ToBoolean(defaultValue.ToString())));
40+
41+
case "NUMERIC":
42+
return JsonResult(eppoClient.GetNumericAssignment(data.Flag, data.SubjectKey, convertedAttributes, Convert.ToDouble(defaultValue.ToString())));
43+
44+
case "JSON":
45+
var jString = defaultValue.ToString();
46+
var defaultJson = JObject.Parse(jString);
47+
return JsonResult(eppoClient.GetJsonAssignment(data.Flag, data.SubjectKey, convertedAttributes, defaultJson));
48+
}
49+
50+
return JsonError("Invalid Assignment Type " + data.AssignmentType);
51+
}
52+
53+
54+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using EppoSDKRelay.DTO;
3+
4+
using eppo_sdk;
5+
6+
namespace EppoSDKRelay.controllers;
7+
8+
[Route("bandits/v1/action")]
9+
public class BanditController : JsonControllerBase
10+
{
11+
[HttpPost]
12+
public ActionResult<string> Post([FromBody] BanditActionRequest data)
13+
{
14+
// If there was a parsing error, this is how it is recorded. We want to fail this tast so return an error
15+
var errors = ModelState.Values.SelectMany(x => x.Errors);
16+
foreach (var err in errors)
17+
{
18+
return JsonError(err.ErrorMessage);
19+
}
20+
21+
var eppoClient = EppoClient.GetInstance();
22+
23+
if (data == null)
24+
{
25+
return JsonError("Data was not parsable");
26+
}
27+
28+
var subject = data.GetSubjectContext();
29+
var actions = data.GetActionContextDict();
30+
var defaultVal = Convert.ToString(data.DefaultValue) ?? "";
31+
32+
var result = eppoClient.GetBanditAction(data.Flag,
33+
subject,
34+
actions,
35+
defaultVal);
36+
return JsonResult(result);
37+
}
38+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
3+
namespace EppoSDKRelay.controllers;
4+
5+
[Route("/")]
6+
public class HealthController : ControllerBase
7+
{
8+
[HttpGet]
9+
public ActionResult<IEnumerable<string>> Check()
10+
{
11+
return new string[] { "hello", "world" };
12+
}
13+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
3+
using EppoSDKRelay.DTO;
4+
using System.Text.Json;
5+
using Newtonsoft.Json.Linq;
6+
7+
namespace EppoSDKRelay.controllers;
8+
9+
public class JsonControllerBase : ControllerBase {
10+
11+
protected static readonly JsonSerializerOptions SerializeOptions = new()
12+
{
13+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
14+
WriteIndented = true
15+
};
16+
17+
protected static ActionResult<string> JsonResult(object result)
18+
{
19+
// System.Text.Json does not play nicely with Newtonsoft types
20+
// Since "Objects" implement IEnumerable, System.Text will try to encode
21+
// the json object as an array. :(
22+
if (result is JObject) {
23+
result = ((JObject)result).ToObject<Dictionary<string, object>>();
24+
}
25+
26+
var response = new TestResponse
27+
{
28+
Result = result
29+
};
30+
return JsonSerializer.Serialize(response, SerializeOptions);
31+
}
32+
33+
protected static ActionResult<string> JsonError(String error)
34+
{
35+
return JsonSerializer.Serialize(new TestResponse
36+
{
37+
Error = error
38+
}, SerializeOptions);
39+
}
40+
41+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using eppo_sdk;
2+
using Microsoft.AspNetCore.Mvc;
3+
4+
namespace EppoSDKRelay.controllers;
5+
6+
[Route("/sdk/reset")]
7+
public class SDKController : ControllerBase
8+
{
9+
// POST sdk/reset
10+
[HttpPost]
11+
public ActionResult<IEnumerable<string>> Reset()
12+
{
13+
// Startup.InitEppoClient();
14+
EppoClient.GetInstance().RefreshConfiguration();
15+
return Ok();
16+
}
17+
18+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace EppoSDKRelay.DTO;
2+
3+
public class Action : AttributeSet
4+
{
5+
public string ActionKey { get; set; }
6+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace EppoSDKRelay.DTO;
2+
3+
public class AssignmentRequest
4+
{
5+
public string Flag { get; set; }
6+
public string SubjectKey { get; set; }
7+
public string AssignmentType { get; set; }
8+
public object DefaultValue { get; set; }
9+
public Dictionary<string, Object> SubjectAttributes { get; set; }
10+
}

0 commit comments

Comments
 (0)