Initial commit
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using RudderSdk.Core.Abstractions;
|
||||
|
||||
namespace RudderSdk.Core;
|
||||
|
||||
public sealed partial class ScenarioService
|
||||
{
|
||||
private void DispatchActiveNode(RuntimeRun run, ActiveNodeState state, bool restored)
|
||||
{
|
||||
var node = FindNode(run.Plan, state.NodeId);
|
||||
if (node == null)
|
||||
{
|
||||
run.ActiveNodes.Remove(state.NodeId);
|
||||
CheckRunCompleted(run);
|
||||
return;
|
||||
}
|
||||
|
||||
var context = new ScenarioNodeContext(this, ToPlanRun(run), node);
|
||||
switch (node.Type ?? string.Empty)
|
||||
{
|
||||
case WaitNode:
|
||||
DispatchWait(run, state, context);
|
||||
break;
|
||||
case RemoteConfigOverrideNode:
|
||||
DispatchRemoteConfigOverride(run, state, context);
|
||||
break;
|
||||
case NotificationNode:
|
||||
EmitNotification(context);
|
||||
break;
|
||||
case StoreNode:
|
||||
EmitStoreOffer(context);
|
||||
break;
|
||||
case QuestNode:
|
||||
EmitQuest(context);
|
||||
break;
|
||||
case LeaderboardNode:
|
||||
EmitLeaderboard(context);
|
||||
break;
|
||||
case BattlePassNode:
|
||||
EmitBattlePass(context);
|
||||
break;
|
||||
case BattlePassLevelNode:
|
||||
EmitBattlePassLevel(context);
|
||||
break;
|
||||
default:
|
||||
_client.Options.Logger?.Log(RudderLogLevel.Warning, $"[Rudder] Unsupported scenario node type '{node.Type}' ({node.Id}).");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DispatchWait(RuntimeRun run, ActiveNodeState state, ScenarioNodeContext context)
|
||||
{
|
||||
if (!state.WaitDeadlineUtc.HasValue)
|
||||
{
|
||||
// Prefer server-provided WaitDeadline from the plan boundary over local calculation.
|
||||
// The server stamps WaitDeadline on server-enforced wait boundaries (see StampBoundaries).
|
||||
var boundary = run.Plan.BoundaryNodes?.FirstOrDefault(
|
||||
b => b.SourceNodeId == state.NodeId && !string.IsNullOrEmpty(b.WaitDeadline)
|
||||
);
|
||||
if (boundary != null && DateTimeOffset.TryParse(boundary.WaitDeadline, null, DateTimeStyles.RoundtripKind, out var parsed))
|
||||
{
|
||||
state.WaitDeadlineUtc = parsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
var delay = GetWaitDelay(context.Data);
|
||||
state.WaitDeadlineUtc = _client.Clock.UtcNow.Add(delay);
|
||||
}
|
||||
Persist();
|
||||
}
|
||||
|
||||
var session = new WaitSession(context, state.WaitDeadlineUtc.Value);
|
||||
OnWait?.Invoke(session);
|
||||
|
||||
if (_client.Clock.UtcNow >= state.WaitDeadlineUtc.Value)
|
||||
_ = CompleteNodeAsync(run.RunId, state.NodeId, "onComplete");
|
||||
}
|
||||
|
||||
private void DispatchRemoteConfigOverride(RuntimeRun run, ActiveNodeState state, ScenarioNodeContext context)
|
||||
{
|
||||
var patches = context.Data["patches"] as JArray;
|
||||
if (patches != null)
|
||||
{
|
||||
foreach (var patchToken in patches.OfType<JObject>())
|
||||
{
|
||||
var key = patchToken.Value<string>("path");
|
||||
if (string.IsNullOrEmpty(key))
|
||||
continue;
|
||||
|
||||
var valueType = patchToken.Value<string>("valueType") ?? "json";
|
||||
var value = SerializeRemoteConfigValue(patchToken["value"], valueType);
|
||||
_client.RemoteConfig.ApplyOverride(key, value, valueType);
|
||||
}
|
||||
}
|
||||
|
||||
var session = new ConfigChangedSession(context);
|
||||
OnConfigChanged?.Invoke(session);
|
||||
_ = CompleteNodeAsync(run.RunId, state.NodeId, "output");
|
||||
}
|
||||
|
||||
private void EmitNotification(ScenarioNodeContext context)
|
||||
{
|
||||
OnNotification?.Invoke(new NotificationSession(context));
|
||||
}
|
||||
|
||||
private void EmitStoreOffer(ScenarioNodeContext context)
|
||||
{
|
||||
OnStoreOffer?.Invoke(new StoreOfferSession(context));
|
||||
}
|
||||
|
||||
private void EmitQuest(ScenarioNodeContext context)
|
||||
{
|
||||
OnQuest?.Invoke(new QuestSession(context));
|
||||
}
|
||||
|
||||
private void EmitLeaderboard(ScenarioNodeContext context)
|
||||
{
|
||||
OnLeaderboard?.Invoke(new LeaderboardSession(context));
|
||||
}
|
||||
|
||||
private void EmitBattlePass(ScenarioNodeContext context)
|
||||
{
|
||||
OnBattlePass?.Invoke(new BattlePassSession(context));
|
||||
}
|
||||
|
||||
private void EmitBattlePassLevel(ScenarioNodeContext context)
|
||||
{
|
||||
OnBattlePassLevel?.Invoke(new BattlePassLevelSession(context));
|
||||
}
|
||||
|
||||
private static TimeSpan GetWaitDelay(JObject data)
|
||||
{
|
||||
var duration = data.Value<double?>("duration") ?? 0;
|
||||
var unit = data.Value<string>("unit") ?? "seconds";
|
||||
if (duration <= 0)
|
||||
return TimeSpan.Zero;
|
||||
|
||||
switch (unit)
|
||||
{
|
||||
case "days":
|
||||
case "day":
|
||||
case "d":
|
||||
return TimeSpan.FromDays(duration);
|
||||
case "hours":
|
||||
case "hour":
|
||||
case "hr":
|
||||
case "h":
|
||||
return TimeSpan.FromHours(duration);
|
||||
case "minutes":
|
||||
case "minute":
|
||||
case "min":
|
||||
case "m":
|
||||
return TimeSpan.FromMinutes(duration);
|
||||
case "seconds":
|
||||
case "second":
|
||||
case "sec":
|
||||
case "s":
|
||||
return TimeSpan.FromSeconds(duration);
|
||||
default:
|
||||
return TimeSpan.FromSeconds(duration);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? SerializeRemoteConfigValue(JToken? token, string valueType)
|
||||
{
|
||||
if (token == null || token.Type == JTokenType.Null)
|
||||
return null;
|
||||
|
||||
switch ((valueType ?? string.Empty).ToLowerInvariant())
|
||||
{
|
||||
case "string":
|
||||
return token.Type == JTokenType.String ? token.Value<string>() : token.ToString(Formatting.None);
|
||||
case "bool":
|
||||
case "boolean":
|
||||
return token.Value<bool>().ToString().ToLowerInvariant();
|
||||
case "int":
|
||||
case "integer":
|
||||
return token.Value<long>().ToString(CultureInfo.InvariantCulture);
|
||||
case "float":
|
||||
case "double":
|
||||
return token.Value<double>().ToString(CultureInfo.InvariantCulture);
|
||||
default:
|
||||
return token.Type == JTokenType.String ? token.Value<string>() : token.ToString(Formatting.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user