/// <inheritdoc/> public FeatureFlagsState AllFlagsState(User user, params FlagsStateOption[] options) { if (IsOffline()) { Log.Warn("AllFlagsState() was called when client is in offline mode. Returning empty state."); return(new FeatureFlagsState(false)); } if (!Initialized()) { if (_featureStore.Initialized()) { Log.Warn("AllFlagsState() called before client initialized; using last known values from feature store"); } else { Log.Warn("AllFlagsState() called before client initialized; feature store unavailable, returning empty state"); return(new FeatureFlagsState(false)); } } if (user == null || user.Key == null) { Log.Warn("AllFlagsState() called with null user or null user key. Returning empty state"); return(new FeatureFlagsState(false)); } var builder = new FeatureFlagsStateBuilder(options); var clientSideOnly = FlagsStateOption.HasOption(options, FlagsStateOption.ClientSideOnly); IDictionary <string, FeatureFlag> flags = _featureStore.All(VersionedDataKind.Features); foreach (KeyValuePair <string, FeatureFlag> pair in flags) { var flag = pair.Value; if (clientSideOnly && !flag.ClientSide) { continue; } try { FeatureFlag.EvalResult result = flag.Evaluate(user, _featureStore, EventFactory.Default); builder.AddFlag(flag.Key, result.Result.Value.InnerValue, result.Result.VariationIndex, result.Result.Reason, flag.Version, flag.TrackEvents, flag.DebugEventsUntilDate); } catch (Exception e) { Log.ErrorFormat("Exception caught for feature flag \"{0}\" when evaluating all flags: {1}", flag.Key, Util.ExceptionMessage(e)); Log.Debug(e.ToString(), e); EvaluationReason reason = EvaluationReason.ErrorReason(EvaluationErrorKind.EXCEPTION); builder.AddFlag(flag.Key, null, null, reason, flag.Version, flag.TrackEvents, flag.DebugEventsUntilDate); } } return(builder.Build()); }
internal EvalResult Evaluate(User user, IFeatureStore featureStore, EventFactory eventFactory) { IList <FeatureRequestEvent> prereqEvents = new List <FeatureRequestEvent>(); if (user == null || user.Key == null) { Log.WarnFormat("User or user key is null when evaluating flag: {0} returning null", Key); return(new EvalResult( new EvaluationDetail <LdValue>(LdValue.Null, null, EvaluationReason.ErrorReason(EvaluationErrorKind.USER_NOT_SPECIFIED)), prereqEvents)); } var details = Evaluate(user, featureStore, prereqEvents, eventFactory); return(new EvalResult(details, prereqEvents)); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { LdValue o = (LdValue)LdValueSerializer.Instance.ReadJson(reader, typeof(LdValue), LdValue.Null, serializer); if (o.IsNull) { return(null); } EvaluationReasonKind kind = (EvaluationReasonKind)Enum.Parse(typeof(EvaluationReasonKind), o.Get("kind").AsString); switch (kind) { case EvaluationReasonKind.OFF: return(EvaluationReason.OffReason); case EvaluationReasonKind.FALLTHROUGH: return(EvaluationReason.FallthroughReason); case EvaluationReasonKind.TARGET_MATCH: return(EvaluationReason.TargetMatchReason); case EvaluationReasonKind.RULE_MATCH: var index = o.Get("ruleIndex").AsInt; var id = o.Get("ruleId").AsString; return(EvaluationReason.RuleMatchReason(index, id)); case EvaluationReasonKind.PREREQUISITE_FAILED: var key = o.Get("prerequisiteKey").AsString; return(EvaluationReason.PrerequisiteFailedReason(key)); case EvaluationReasonKind.ERROR: var errorKind = (EvaluationErrorKind)Enum.Parse(typeof(EvaluationErrorKind), o.Get("errorKind").AsString); return(EvaluationReason.ErrorReason(errorKind)); } throw new ArgumentException(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject o = serializer.Deserialize <JObject>(reader); if (o == null) { return(null); } EvaluationReasonKind kind = o.GetValue("kind").ToObject <EvaluationReasonKind>(); switch (kind) { case EvaluationReasonKind.OFF: return(EvaluationReason.OffReason); case EvaluationReasonKind.FALLTHROUGH: return(EvaluationReason.FallthroughReason); case EvaluationReasonKind.TARGET_MATCH: return(EvaluationReason.TargetMatchReason); case EvaluationReasonKind.RULE_MATCH: var index = (int)o.GetValue("ruleIndex"); var id = (string)o.GetValue("ruleId"); return(EvaluationReason.RuleMatchReason(index, id)); case EvaluationReasonKind.PREREQUISITE_FAILED: var key = (string)o.GetValue("prerequisiteKey"); return(EvaluationReason.PrerequisiteFailedReason(key)); case EvaluationReasonKind.ERROR: var errorKind = o.GetValue("errorKind").ToObject <EvaluationErrorKind>(); return(EvaluationReason.ErrorReason(errorKind)); } throw new ArgumentException(); }
private EvaluationDetail <T> Evaluate <T>(string featureKey, User user, LdValue defaultValue, LdValue.Converter <T> converter, bool checkType, EventFactory eventFactory) { T defaultValueOfType = converter.ToType(defaultValue); if (!Initialized()) { if (_featureStore.Initialized()) { Log.Warn("Flag evaluation before client initialized; using last known values from feature store"); } else { Log.Warn("Flag evaluation before client initialized; feature store unavailable, returning default value"); return(new EvaluationDetail <T>(defaultValueOfType, null, EvaluationReason.ErrorReason(EvaluationErrorKind.CLIENT_NOT_READY))); } } FeatureFlag featureFlag = null; try { featureFlag = _featureStore.Get(VersionedDataKind.Features, featureKey); if (featureFlag == null) { Log.InfoFormat("Unknown feature flag {0}; returning default value", featureKey); _eventProcessor.SendEvent(eventFactory.NewUnknownFeatureRequestEvent(featureKey, user, defaultValue, EvaluationErrorKind.FLAG_NOT_FOUND)); return(new EvaluationDetail <T>(defaultValueOfType, null, EvaluationReason.ErrorReason(EvaluationErrorKind.FLAG_NOT_FOUND))); } if (user == null || user.Key == null) { Log.Warn("Feature flag evaluation called with null user or null user key. Returning default"); _eventProcessor.SendEvent(eventFactory.NewDefaultFeatureRequestEvent(featureFlag, user, defaultValue, EvaluationErrorKind.USER_NOT_SPECIFIED)); return(new EvaluationDetail <T>(defaultValueOfType, null, EvaluationReason.ErrorReason(EvaluationErrorKind.USER_NOT_SPECIFIED))); } FeatureFlag.EvalResult evalResult = featureFlag.Evaluate(user, _featureStore, eventFactory); if (!IsOffline()) { foreach (var prereqEvent in evalResult.PrerequisiteEvents) { _eventProcessor.SendEvent(prereqEvent); } } var evalDetail = evalResult.Result; EvaluationDetail <T> returnDetail; if (evalDetail.VariationIndex == null) { returnDetail = new EvaluationDetail <T>(defaultValueOfType, null, evalDetail.Reason); evalDetail = new EvaluationDetail <LdValue>(defaultValue, null, evalDetail.Reason); } else { if (checkType && !defaultValue.IsNull && evalDetail.Value.Type != defaultValue.Type) { Log.ErrorFormat("Expected type: {0} but got {1} when evaluating FeatureFlag: {2}. Returning default", defaultValue.Type, evalDetail.Value.Type, featureKey); _eventProcessor.SendEvent(eventFactory.NewDefaultFeatureRequestEvent(featureFlag, user, defaultValue, EvaluationErrorKind.WRONG_TYPE)); return(new EvaluationDetail <T>(defaultValueOfType, null, EvaluationReason.ErrorReason(EvaluationErrorKind.WRONG_TYPE))); } returnDetail = new EvaluationDetail <T>(converter.ToType(evalDetail.Value), evalDetail.VariationIndex, evalDetail.Reason); } _eventProcessor.SendEvent(eventFactory.NewFeatureRequestEvent(featureFlag, user, evalDetail, defaultValue)); return(returnDetail); } catch (Exception e) { Log.ErrorFormat("Encountered exception in LaunchDarkly client: {0} when evaluating feature key: {1} for user key: {2}", Util.ExceptionMessage(e), featureKey, user.Key); Log.Debug(e.ToString(), e); var reason = EvaluationReason.ErrorReason(EvaluationErrorKind.EXCEPTION); if (featureFlag == null) { _eventProcessor.SendEvent(eventFactory.NewUnknownFeatureRequestEvent(featureKey, user, defaultValue, EvaluationErrorKind.EXCEPTION)); } else { _eventProcessor.SendEvent(eventFactory.NewFeatureRequestEvent(featureFlag, user, new EvaluationDetail <LdValue>(defaultValue, null, reason), defaultValue)); } return(new EvaluationDetail <T>(defaultValueOfType, null, reason)); } }
/// <summary> /// Equivalent to <see cref="ILdClient.StringVariationDetail(string, User, string)"/>, but converts the /// flag's string value to an enum value. /// </summary> /// <remarks> /// <para> /// If the flag has a value that is not one of the allowed enum value names, or is not a string, /// <c>defaultValue</c> is returned. /// </para> /// <para> /// Note that there is no type constraint to guarantee that T really is an enum type, because that is /// a C# 7.3 feature that is unavailable in older versions of .NET Standard. If you try to use a /// non-enum type, you will simply receive the default value back. /// </para> /// </remarks> /// <typeparam name="T">the enum type</typeparam> /// <param name="client">the client instance</param> /// <param name="key">the unique feature key for the feature flag</param> /// <param name="user">the end user requesting the flag</param> /// <param name="defaultValue">the default value of the flag (as an enum value)</param> /// <returns>an <see cref="EvaluationDetail{T}"/> object</returns> public static EvaluationDetail <T> EnumVariationDetail <T>(this ILdClient client, string key, User user, T defaultValue) { var stringDetail = client.StringVariationDetail(key, user, defaultValue.ToString()); if (stringDetail.Value != null) { try { var enumValue = (T)System.Enum.Parse(typeof(T), stringDetail.Value, true); return(new EvaluationDetail <T>(enumValue, stringDetail.VariationIndex, stringDetail.Reason)); } catch (System.ArgumentException) { return(new EvaluationDetail <T>(defaultValue, stringDetail.VariationIndex, EvaluationReason.ErrorReason(EvaluationErrorKind.WRONG_TYPE))); } } return(new EvaluationDetail <T>(defaultValue, stringDetail.VariationIndex, stringDetail.Reason)); }
internal EvaluationDetail <LdValue> ErrorResult(EvaluationErrorKind kind) { return(new EvaluationDetail <LdValue>(LdValue.Null, null, EvaluationReason.ErrorReason(kind))); }