internal static SegmentRule ReadSegmentRule(ref JReader reader) { ImmutableList <Clause> clauses = null; int? weight = null; UserAttribute?bucketBy = null; for (var obj = reader.Object(); obj.Next(ref reader);) { switch (obj.Name) { case var n when n == "clauses": clauses = SerializationHelpers.ReadClauses(ref reader); break; case var n when n == "weight": weight = reader.IntOrNull(); break; case var n when n == "bucketBy": var s = reader.StringOrNull(); bucketBy = s is null ? (UserAttribute?)null : UserAttribute.ForName(s); break; } } return(new SegmentRule(clauses, weight, bucketBy)); }
public void TestBucketUserByFloatAttr() { var user = User.Builder("userKey").Custom("floatAttr", 999.999F).Build(); var bucket = Bucketing.BucketUser(user, "hashKey", UserAttribute.ForName("floatAttr"), "saltyA"); Assert.Equal(0, bucket, 15); }
public void CanSetValuePerUser() { _td.Update(_td.Flag("flag") .Variations(LdValue.Of("red"), LdValue.Of("green"), LdValue.Of("blue")) .Variation(LdValue.Of("red")) .VariationForUser("user1", LdValue.Of("green")) .VariationForUser("user2", LdValue.Of("blue")) .VariationFunc(user => user.GetAttribute(UserAttribute.ForName("favoriteColor")) )); var user1 = User.WithKey("user1"); var user2 = User.WithKey("user2"); var user3 = User.Builder("user3").Custom("favoriteColor", "green").Build(); using (var client = LdClient.Init(_config, user1, TimeSpan.FromSeconds(1))) { Assert.Equal("green", client.StringVariation("flag", "")); client.Identify(user2, TimeSpan.FromSeconds(1)); Assert.Equal("blue", client.StringVariation("flag", "")); client.Identify(user3, TimeSpan.FromSeconds(1)); Assert.Equal("green", client.StringVariation("flag", "")); } }
/// <summary> /// Marks a set of attribute names as private. /// </summary> /// <remarks> /// <para> /// Any users sent to LaunchDarkly with this configuration active will have attributes with these /// names removed. This is in addition to any attributes that were marked as private for an /// individual user with <see cref="UserBuilder"/> methods. /// </para> /// <para> /// Using <see cref="PrivateAttributes(UserAttribute[])"/> is preferable to avoid the possibility of /// misspelling a built-in attribute. /// </para> /// </remarks> /// <param name="attributes">a set of names that will be removed from user data set to LaunchDarkly</param> /// <returns>the builder</returns> /// <seealso cref="PrivateAttributes(UserAttribute[])"/> public EventProcessorBuilder PrivateAttributeNames(params string[] attributes) { foreach (var a in attributes) { _privateAttributes.Add(UserAttribute.ForName(a)); } return(this); }
public void CanBucketByIntAttributeSameAsString() { var user = User.Builder("key") .Custom("stringattr", "33333") .Custom("intattr", 33333) .Build(); var resultForString = Bucketing.BucketUser(null, user, "key", UserAttribute.ForName("stringattr"), "salt"); var resultForInt = Bucketing.BucketUser(null, user, "key", UserAttribute.ForName("intattr"), "salt"); Assert.Equal((double)resultForInt, (double)resultForString, 10); }
public void PrivateAttributeNames() { var b = _tester.New(); Assert.Empty(b._privateAttributes); b.PrivateAttributeNames("name"); b.PrivateAttributeNames("email", "other"); Assert.Equal(new HashSet <UserAttribute> { UserAttribute.Name, UserAttribute.Email, UserAttribute.ForName("other") }, b._privateAttributes); }
public void TestBucketUserByIntAttr() { var user = User.Builder("userKey").Custom("intAttr", 33333).Build(); var bucket = Bucketing.BucketUser(user, "hashKey", UserAttribute.ForName("intAttr"), "saltyA"); Assert.Equal(0.54771423, bucket, 7); user = User.Builder("userKey").Custom("stringAttr", "33333").Build(); var bucket2 = Bucketing.BucketUser(user, "hashKey", UserAttribute.ForName("stringAttr"), "saltyA"); Assert.Equal(bucket, bucket2, 15); }
private void TestPrivateAttribute(string privateAttrName, bool globallyPrivate) { var builder = User.Builder("userkey") .Anonymous(true) .Secondary("s"); var topJsonBuilder = LdValue.BuildObject() .Add("key", "userkey") .Add("anonymous", true) .Add("secondary", "s"); var customJsonBuilder = LdValue.BuildObject(); Action<string, Func<string, IUserBuilderCanMakeAttributePrivate>, string, LdValue.ObjectBuilder> setAttr = (attrName, setter, value, jsonBuilder) => { if (attrName == privateAttrName) { if (globallyPrivate) { setter(value); } else { setter(value).AsPrivateAttribute(); } } else { setter(value); jsonBuilder.Add(attrName, value); } }; setAttr("avatar", builder.Avatar, "http://avatar", topJsonBuilder); setAttr("country", builder.Country, "US", topJsonBuilder); setAttr("custom1", v => builder.Custom("custom1", v), "value1", customJsonBuilder); setAttr("custom2", v => builder.Custom("custom2", v), "value2", customJsonBuilder); setAttr("email", builder.Email, "*****@*****.**", topJsonBuilder); setAttr("firstName", builder.FirstName, "first", topJsonBuilder); setAttr("ip", builder.IPAddress, "1.2.3.4", topJsonBuilder); setAttr("lastName", builder.LastName, "last", topJsonBuilder); setAttr("name", builder.Name, "me", topJsonBuilder); topJsonBuilder.Add("custom", customJsonBuilder.Build()); topJsonBuilder.Add("privateAttrs", LdValue.ArrayOf(LdValue.Of(privateAttrName))); var userJson = topJsonBuilder.Build(); var config = new EventsConfiguration(); if (globallyPrivate) { config.PrivateAttributeNames = ImmutableHashSet.Create<UserAttribute>( UserAttribute.ForName(privateAttrName)); }; TestInlineUserSerialization(builder.Build(), userJson, config); }
public EventUser Build() { _result.Key = _user.Key; _result.Secondary = StringAttrIfNotPrivate(UserAttribute.Secondary); _result.Anonymous = _user.Anonymous ? (bool?)true : null; _result.IPAddress = StringAttrIfNotPrivate(UserAttribute.IPAddress); _result.Country = StringAttrIfNotPrivate(UserAttribute.Country); _result.FirstName = StringAttrIfNotPrivate(UserAttribute.FirstName); _result.LastName = StringAttrIfNotPrivate(UserAttribute.LastName); _result.Name = StringAttrIfNotPrivate(UserAttribute.Name); _result.Avatar = StringAttrIfNotPrivate(UserAttribute.Avatar); _result.Email = StringAttrIfNotPrivate(UserAttribute.Email); // With the custom attributes, for efficiency's sake we would like to reuse the same ImmutableDictionary // whenever possible. So, we'll lazily create a new collection only if it turns out that there are any // changes needed (i.e. if one of the custom attributes turns out to be private). ImmutableDictionary <string, LdValue> .Builder customAttrsBuilder = null; foreach (var kv in _user.Custom) { if (!CheckPrivateAttr(UserAttribute.ForName(kv.Key))) { if (customAttrsBuilder is null) { // This is the first private custom attribute we've found. Lazily create the builder // by first copying all of the ones we've already iterated over. We can rely on the // iteration order being the same because it's immutable. customAttrsBuilder = ImmutableDictionary.CreateBuilder <string, LdValue>(); foreach (var kv1 in _user.Custom) { if (kv1.Key == kv.Key) { break; } customAttrsBuilder[kv1.Key] = kv1.Value; } } } else { // It's not a private attribute. if (customAttrsBuilder != null) { customAttrsBuilder[kv.Key] = kv.Value; } } } var custom = customAttrsBuilder is null ? _user.Custom : customAttrsBuilder.ToImmutable(); _result.Custom = custom.Count == 0 ? null : custom; _result.PrivateAttrs = _privateAttrs is null ? null : _privateAttrs.ToImmutable(); return(_result); }
public static void UsersEqualExcludingAutoProperties(User expected, User actual) { var builder = User.Builder(expected); foreach (var autoProp in new string[] { "device", "os" }) { if (!actual.GetAttribute(UserAttribute.ForName(autoProp)).IsNull) { builder.Custom(autoProp, actual.GetAttribute(UserAttribute.ForName(autoProp))); } } UsersEqual(builder.Build(), actual); }
internal static Rollout?ReadRollout(ref JReader r) { ImmutableList <WeightedVariation> variations = null; UserAttribute?bucketBy = null; var obj = r.ObjectOrNull(); if (!obj.IsDefined) { return(null); } while (obj.Next(ref r)) { switch (obj.Name) { case var n when n == "variations": var listBuilder = ImmutableList.CreateBuilder <WeightedVariation>(); for (var arr = r.ArrayOrNull(); arr.Next(ref r);) { int variation = 0, weight = 0; for (var wvObj = r.Object(); wvObj.Next(ref r);) { switch (wvObj.Name) { case var nn when nn == "variation": variation = r.Int(); break; case var nn when nn == "weight": weight = r.Int(); break; } } listBuilder.Add(new WeightedVariation(variation, weight)); } variations = listBuilder.ToImmutable(); break; case var n when n == "bucketBy": var s = r.StringOrNull(); bucketBy = s is null ? (UserAttribute?)null : UserAttribute.ForName(s); break; } } return(new Rollout(variations, bucketBy)); }
public void CannotBucketByOtherDataTypes() { foreach (var attributeValue in new LdValue[] { LdValue.Null, LdValue.Of(true), LdValue.Of(33333.5) }) { var user = User.Builder("key") .Custom("badattr", attributeValue) .Build(); var result = Bucketing.BucketUser(null, user, "key", UserAttribute.ForName("badattr"), "salt"); if (result != 0f) { Assert.True(false, "got unexpected value " + result + " for attribute value " + attributeValue); } } }
internal static ImmutableList <Clause> ReadClauses(ref JReader r) { var builder = ImmutableList.CreateBuilder <Clause>(); for (var arr = r.ArrayOrNull(); arr.Next(ref r);) { UserAttribute attribute = new UserAttribute(); Operator op = null; ImmutableList <LdValue> values = null; bool negate = false; for (var obj = r.Object(); obj.Next(ref r);) { switch (obj.Name) { case var n when n == "attribute": attribute = UserAttribute.ForName(r.String()); break; case var n when n == "op": op = Operator.ForName(r.String()); // Operator.ForName never returns null - unrecognized operators return a stub object break; case var n when n == "values": values = ReadValues(ref r); break; case var n when n == "negate": negate = r.Bool(); break; } } builder.Add(new Clause(attribute, op, values, negate)); } return(builder.ToImmutable()); }
internal static Rollout?ReadRollout(ref JReader r) { ImmutableList <WeightedVariation> variations = null; UserAttribute?bucketBy = null; RolloutKind kind = RolloutKind.Rollout; int? seed = null; var obj = r.ObjectOrNull(); if (!obj.IsDefined) { return(null); } while (obj.Next(ref r)) { switch (obj.Name) { case var n when n == "variations": var listBuilder = ImmutableList.CreateBuilder <WeightedVariation>(); for (var arr = r.ArrayOrNull(); arr.Next(ref r);) { int variation = 0, weight = 0; bool untracked = false; for (var wvObj = r.Object(); wvObj.Next(ref r);) { switch (wvObj.Name) { case var nn when nn == "variation": variation = r.Int(); break; case var nn when nn == "weight": weight = r.Int(); break; case var nn when nn == "untracked": untracked = r.Bool(); break; } } listBuilder.Add(new WeightedVariation(variation, weight, untracked)); } variations = listBuilder.ToImmutable(); break; case var n when n == "bucketBy": var s = r.StringOrNull(); bucketBy = s is null ? (UserAttribute?)null : UserAttribute.ForName(s); break; case var n when n == "kind": var kindStr = r.StringOrNull(); kind = "experiment".Equals(kindStr) ? RolloutKind.Experiment : RolloutKind.Rollout; break; case var n when n == "seed": seed = r.IntOrNull(); break; } } return(new Rollout(kind, seed, variations, bucketBy)); }
public ClauseBuilder Attribute(string attribute) => Attribute(UserAttribute.ForName(attribute));