Skip to content
Permalink
Browse files

use ConfigurationValue to store exceptions instead of dictionary (#1135)

  • Loading branch information...
nataly87s committed Apr 14, 2019
1 parent 7bc02fe commit ae9494606e561e9f88022f58fe3e175d61c4118b
@@ -1,4 +1,5 @@
using FSharpUtils.Newtonsoft;
using System;
using FSharpUtils.Newtonsoft;
using LanguageExt;
using System.Collections.Generic;
using System.Linq;
@@ -35,12 +36,20 @@ public static GetRuleValue GetRulesEvaluator(IdentityHashSet identities, GetLoad

getRuleValue = Memoize(path =>
{
foreach (var identity in identityTypes)
try
{
var fixedResult = ContextHelpers.GetFixedConfigurationContext(context, identity)(path);
if (fixedResult.IsSome) return fixedResult;
foreach (var identity in identityTypes)
{
var fixedResult = ContextHelpers.GetFixedConfigurationContext(context, identity)(path);
if (fixedResult.IsSome) return fixedResult;
}

return getRule(path).Bind(x => x.GetValue(context));
}
catch (Exception e)
{
return ConfigurationValue.Error(e);
}
return getRule(path).Bind(x => x.GetValue(context));
});
return getRuleValue;
}
@@ -1,21 +1,35 @@
using System;
using FSharpUtils.Newtonsoft;

namespace Tweek.Engine.DataTypes
{
public struct ConfigurationValue
{
public readonly JsonValue Value;
public readonly Exception Exception;

public ConfigurationValue(JsonValue value)
{
Value = value;
Exception = null;
}

public ConfigurationValue(Exception exception)
{
Value = JsonValue.Null;
Exception = exception;
}

public static ConfigurationValue New(JsonValue value)
{
return new ConfigurationValue(value);
}

public static ConfigurationValue Error(Exception exception)
{
return new ConfigurationValue(exception);
}

public override string ToString()
{
return Value.ToString();
@@ -58,13 +58,13 @@ public async Task CalculateSingleValue()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("_", NoIdentities, context);
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());

val = await tweek.GetContextAndCalculate("abc/_", NoIdentities, context);
Assert.Equal( "SomeValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal( "SomeValue", val["abc/somepath"].Value.AsString());

val = await tweek.GetContextAndCalculate("abc/somepath", NoIdentities, context);
Assert.Equal( "SomeValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal( "SomeValue", val["abc/somepath"].Value.AsString());
});
}

@@ -78,7 +78,7 @@ public async Task CalculateMultipleValues()

await Run(async (tweek, context) =>
{
var val = (await tweek.GetContextAndCalculate("abc/_", NoIdentities, context)).Data;
var val = await tweek.GetContextAndCalculate("abc/_", NoIdentities, context);
Assert.Equal(3, val.Count);
Assert.Equal("SomeValue",val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue",val["abc/otherpath"].Value.AsString());
@@ -96,7 +96,7 @@ public async Task CalculateMultiplePathQueries()

await Run(async (tweek, context) =>
{
var val = (await tweek.GetContextAndCalculate(new List<ConfigurationPath>{"abc/_", "def/_"}, NoIdentities, context)).Data;
var val = await tweek.GetContextAndCalculate(new List<ConfigurationPath>{"abc/_", "def/_"}, NoIdentities, context);
Assert.Equal(4, val.Count);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/otherpath"].Value.AsString());
@@ -115,7 +115,7 @@ public async Task CalculateMultiplePathQueriesWithOverlap()

await Run(async (tweek, context) =>
{
var val = (await tweek.GetContextAndCalculate(new List<ConfigurationPath> { "abc/_", "abc/nested/_" }, NoIdentities, context)).Data;
var val = await tweek.GetContextAndCalculate(new List<ConfigurationPath> { "abc/_", "abc/nested/_" }, NoIdentities, context);
Assert.Equal(3, val.Count);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/otherpath"].Value.AsString());
@@ -142,13 +142,13 @@ public async Task CalculateFilterByMatcher()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal(0, val.Data.Count);
Assert.Equal(0, val.Count);

val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context);
Assert.Equal(0, val.Data.Count);
Assert.Equal(0, val.Count);

val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "3") }, context);
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
});
}

@@ -171,13 +171,13 @@ public async Task CalculateFilterByMatcherWithMultiIdentities()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal(0, val.Data.Count);
Assert.Equal(0, val.Count);

val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("user", "1") }, context);
Assert.Equal(0, val.Data.Count);
Assert.Equal(0, val.Count);

val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1"), new Identity("user", "1") }, context);
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
});
}

@@ -196,7 +196,7 @@ public async Task MultipleRules()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal( "SomeValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal( "SomeValue", val["abc/somepath"].Value.AsString());
});
}

@@ -222,7 +222,7 @@ public async Task MultipleRulesWithFallback()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
});
}

@@ -248,14 +248,14 @@ public async Task CalculateWithMultiVariant()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { }, context);
Assert.Equal(0, val.Data.Count);
Assert.Equal(0, val.Count);
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1")}, context);
Assert.True(val.Data["abc/somepath"].Value.AsString() == "true" || val.Data["abc/somepath"].Value.AsString() == "false");
Assert.True(val["abc/somepath"].Value.AsString() == "true" || val["abc/somepath"].Value.AsString() == "false");
await Task.WhenAll(Enumerable.Range(0, 10).Select(async x =>
{
var expected = val.Data["abc/somepath"].Value;
var expected = val["abc/somepath"].Value;
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> {new Identity("device", "1")}, context);
Assert.Equal(val.Data["abc/somepath"].Value, expected);
Assert.Equal(val["abc/somepath"].Value, expected);
}));
});
}
@@ -278,7 +278,7 @@ public async Task ContextKeysShouldBeCaseInsensitive()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal("true", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("true", val["abc/somepath"].Value.AsString());
});
}

@@ -308,10 +308,10 @@ public async Task RuleUsingTimeBasedOperators()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal("true", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("true", val["abc/somepath"].Value.AsString());

val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context);
Assert.Equal("false", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("false", val["abc/somepath"].Value.AsString());

});
}
@@ -336,13 +336,13 @@ public async Task CalculateWithFixedValue()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal("FixedValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("FixedValue", val["abc/somepath"].Value.AsString());

val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context);
Assert.Equal("RuleBasedValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("RuleBasedValue", val["abc/somepath"].Value.AsString());

val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "3") }, context);
Assert.Equal("FixedValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("FixedValue", val["abc/somepath"].Value.AsString());

});
}
@@ -373,11 +373,11 @@ public async Task CalculateWithRecursiveMatcher()

await Run(async (tweek, context) =>
{
var val = (await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context)).Data;
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal(1, val.Count);
Assert.Equal("true", val["abc/dep_path1"].Value.AsString());

val = (await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context)).Data;
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context);
Assert.Equal(3, val.Count);
Assert.Equal("true", val["abc/dep_path1"].Value.AsString());
Assert.Equal("true", val["abc/dep_path2"].Value.AsString());
@@ -401,7 +401,7 @@ public async Task EmptyFixedKeyIsIgnoredInScan()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("_", new HashSet<Identity> { new Identity("device", "1")}, context);
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
});
}

@@ -424,25 +424,18 @@ public async Task BadKeyShouldReturnError()
var identities = new HashSet<Identity> {new Identity("device", "1")};

var val = await tweek.GetContextAndCalculate("_", identities, context);
Assert.Equal(1, val.Errors.Count);
Assert.True(val.Errors.ContainsKey("abc/bad_path"));
Assert.False(val.Data.ContainsKey("abc/bad_path"));
Assert.Equal("SomeValue", val.Data["abc/good_path"].Value.AsString());
Assert.NotNull(val["abc/bad_path"].Exception);
Assert.Equal(JsonValue.Null, val["abc/bad_path"].Value);
Assert.Equal("SomeValue", val["abc/good_path"].Value.AsString());

val = await tweek.GetContextAndCalculate("abc/_", identities, context);
Assert.Equal(1, val.Errors.Count);
Assert.True(val.Errors.ContainsKey("abc/bad_path"));
Assert.False(val.Data.ContainsKey("abc/bad_path"));
Assert.Equal( "SomeValue", val.Data["abc/good_path"].Value.AsString());

val = await tweek.GetContextAndCalculate("abc/good_path", identities, context);
Assert.Equal(0, val.Errors.Count);
Assert.Equal( "SomeValue", val.Data["abc/good_path"].Value.AsString());
Assert.NotNull(val["abc/bad_path"].Exception);
Assert.Equal(JsonValue.Null, val["abc/bad_path"].Value);
Assert.Equal("SomeValue", val["abc/good_path"].Value.AsString());

val = await tweek.GetContextAndCalculate("abc/bad_path", identities, context);
Assert.Equal(1, val.Errors.Count);
Assert.True(val.Errors.ContainsKey("abc/bad_path"));
Assert.Equal(0, val.Data.Count);
Assert.NotNull(val["abc/bad_path"].Exception);
Assert.Equal(JsonValue.Null, val["abc/bad_path"].Value);
});
}
}
@@ -7,7 +7,7 @@ namespace Tweek.Engine
{
public interface ITweek
{
TweekValuesResult Calculate(
Dictionary<ConfigurationPath, ConfigurationValue> Calculate(
ICollection<ConfigurationPath> pathQuery,
IdentityHashSet identities, GetLoadedContextByIdentityType context,
ConfigurationPath[] includeFixedPaths = null);
@@ -22,7 +22,7 @@ public static Option<JsonValue> SingleKey(this IDictionary<ConfigurationPath, Co
return Prelude.None;
}

public static Task<TweekValuesResult> GetContextAndCalculate(this ITweek tweek,
public static Task<Dictionary<ConfigurationPath, ConfigurationValue>> GetContextAndCalculate(this ITweek tweek,
ConfigurationPath pathQuery,
IdentityHashSet identities,
IContextReader contextDriver,
@@ -31,7 +31,7 @@ public static Option<JsonValue> SingleKey(this IDictionary<ConfigurationPath, Co
return tweek.GetContextAndCalculate(new[] { pathQuery }, identities, contextDriver, externalContext);
}

public static async Task<TweekValuesResult> GetContextAndCalculate(this ITweek tweek,
public static async Task<Dictionary<ConfigurationPath, ConfigurationValue>> GetContextAndCalculate(this ITweek tweek,
ICollection<ConfigurationPath> pathQuery,
IdentityHashSet identities,
IContextReader contextDriver,
@@ -59,11 +59,20 @@ public static Option<JsonValue> SingleKey(this IDictionary<ConfigurationPath, Co
return tweek.Calculate(pathQuery, identities, context, contextPaths);
}

public static TweekValuesResult Calculate(this ITweek tweek,
public static Dictionary<ConfigurationPath, ConfigurationValue> Calculate(this ITweek tweek,
ConfigurationPath pathQuery,
IdentityHashSet identities, GetLoadedContextByIdentityType context, ConfigurationPath[] includeFixedPaths = null)
{
return tweek.Calculate(new[] { pathQuery }, identities, context, includeFixedPaths);
}

public static void EnsureSuccess(this Dictionary<ConfigurationPath, ConfigurationValue> result)
{
var errors = result.Values.Where(x => x.Exception != null).Select(x => x.Exception).ToArray();
if (errors.Length != 0)
{
throw new AggregateException(errors);
}
}
}
}
@@ -18,7 +18,7 @@ public TweekRunner(Func<(GetRule, PathExpander)> rulesLoader)
_rulesLoader = rulesLoader;
}

public TweekValuesResult Calculate(
public Dictionary<ConfigurationPath, ConfigurationValue> Calculate(
ICollection<ConfigurationPath> pathQuery,
HashSet<Identity> identities,
GetLoadedContextByIdentityType context,
@@ -36,19 +36,12 @@ public TweekRunner(Func<(GetRule, PathExpander)> rulesLoader)

var paths = include.Concat(expandItems).Concat(pathQuery.Where(t => !t.IsScan)).Distinct();

var result = new TweekValuesResult();
var result = new Dictionary<ConfigurationPath, ConfigurationValue>();

foreach (var path in paths)
{
try
{
var ruleValue = getRuleValue(path);
ruleValue.IfSome(value => result.Data[path] = value);
}
catch (Exception e)
{
result.Errors[path] = e;
}
var ruleValue = getRuleValue(path);
ruleValue.IfSome(value => result[path] = value);
}

return result;

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.
Oops, something went wrong.

0 comments on commit ae94946

Please sign in to comment.
You can’t perform that action at this time.