package evaluation import ( "github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel" "github.com/launchdarkly/go-sdk-common/v3/ldcontext" "github.com/launchdarkly/go-sdk-common/v3/ldreason" "github.com/launchdarkly/go-sdk-common/v3/ldvalue" ) // Evaluator is the engine for evaluating feature flags. type Evaluator interface { // Evaluate evaluates a feature flag for the specified context. // // The flag is passed by reference only for efficiency; the evaluator will never modify any flag // properties. Passing a nil flag will result in a panic. // // The evaluator does not know anything about analytics events; generating any appropriate analytics // events is the responsibility of the caller, who can also provide a callback in prerequisiteFlagEventRecorder // to be notified if any additional evaluations were done due to prerequisites. The prerequisiteFlagEventRecorder // parameter can be nil if you do not need to track prerequisite evaluations. Evaluate( flag *ldmodel.FeatureFlag, context ldcontext.Context, prerequisiteFlagEventRecorder PrerequisiteFlagEventRecorder, ) Result } // PrerequisiteFlagEventRecorder is a function that Evaluator.Evaluate() will call to record the // result of a prerequisite flag evaluation. type PrerequisiteFlagEventRecorder func(PrerequisiteFlagEvent) // PrerequisiteFlagEvent is the parameter data passed to PrerequisiteFlagEventRecorder. type PrerequisiteFlagEvent struct { // TargetFlagKey is the key of the feature flag that had a prerequisite. TargetFlagKey string // Context is the context that the flag was evaluated for. We pass this back to the caller, even though the caller // already passed it to us in the Evaluate parameters, so that the caller can provide a stateless function for // PrerequisiteFlagEventRecorder rather than a closure (since closures are less efficient). Context ldcontext.Context // PrerequisiteFlag is the full configuration of the prerequisite flag. We need to pass the full flag here rather // than just the key because the flag's properties (such as TrackEvents) can affect how events are generated. // This is passed by reference for efficiency only, and will never be nil; the PrerequisiteFlagEventRecorder // must not modify the flag's properties. PrerequisiteFlag *ldmodel.FeatureFlag // PrerequisiteResult is the result of evaluating the prerequisite flag. PrerequisiteResult Result } // DataProvider is an abstraction for querying feature flags and user segments from a data store. // The caller provides an implementation of this interface to NewEvaluator. // // Flags and segments are returned by reference for efficiency only (on the assumption that the // caller already has these objects in memory); the evaluator will never modify their properties. type DataProvider interface { // GetFeatureFlag attempts to retrieve a feature flag from the data store by key. // // The evaluator calls this method if a flag contains a prerequisite condition referencing // another flag. // // The method returns nil if the flag was not found. The DataProvider should treat any deleted // flag as "not found" even if the data store contains a deleted flag placeholder for it. GetFeatureFlag(key string) *ldmodel.FeatureFlag // GetSegment attempts to retrieve a user segment from the data store by key. // // The evaluator calls this method if a clause in a flag rule uses the OperatorSegmentMatch // test. // // The method returns nil if the segment was not found. The DataProvider should treat any deleted // segment as "not found" even if the data store contains a deleted segment placeholder for it. GetSegment(key string) *ldmodel.Segment } // BigSegmentProvider is an abstraction for querying membership in big segments. The caller // provides an implementation of this interface to NewEvaluatorWithBigSegments. type BigSegmentProvider interface { // GetMembership queries a snapshot of the current segment state for a specific context // key. // // The underlying big segment store implementation will use a hash of the context key, rather // than the raw key. But computing the hash is the responsibility of the BigSegmentProvider // implementation rather than the evaluator, because there may already have a cached result for // that user, and we don't want to have to compute a hash repeatedly just to query a cache. // // Any given big segment is specific to one context kind, so we do not specify a context kind // here; it is OK for the membership results to include different context kinds for the same // key. That is, if for instance the context {kind: "user", key: "x"} is included in big segment // S1, and the context {kind: "org", key: "x"} is included in big segment S2, then the query // result for key "x" will show that it is included in both S1 and S2; even though those "x" // keys are really for two unrelated context kinds, we will always know which kind we mean if // we are specifically checking either S1 or S2, because S1 is defined as only applying to the // "user" kind and S2 is defined as only applying to the "org" kind. // // If the returned BigSegmentMembership is nil, it is treated the same as an implementation // whose CheckMembership method always returns an empty value. GetMembership( contextKey string, ) (BigSegmentMembership, ldreason.BigSegmentsStatus) } // BigSegmentMembership is the return type of BigSegmentProvider.GetContextMembership(). It is // associated with a single context kind and context key, and provides the ability to check whether // that context is included in or excluded from any number of big segments. // // This is an immutable snapshot of the state for this context at the time GetBigSegmentMembership // was called. Calling CheckMembership should not cause the state to be queried again. The object // should be safe for concurrent access by multiple goroutines. // // This interface also exists in go-server-sdk because it is exposed as part of the public SDK API; // users can write their own implementations of SDK components, but we do not want application code // to reference go-server-sdk-evaluation symbols directly as part of that, because this library is // versioned separately from the SDK. Currently the two interfaces are identical, but it might be // that the go-server-sdk-evaluation version would diverge from the go-server-sdk version due to // some internal requirements that aren't relevant to users, in which case go-server-sdk would be // responsible for bridging the difference. type BigSegmentMembership interface { // CheckMembership tests whether the user is explicitly included or explicitly excluded in the // specified segment, or neither. The segment is identified by a segmentRef which is not the // same as the segment key-- it includes the key but also versioning information that the SDK // will provide. The store implementation should not be concerned with the format of this. // // If the user is explicitly included (regardless of whether the user is also explicitly // excluded or not-- that is, inclusion takes priority over exclusion), the method returns an // OptionalBool with a true value. // // If the user is explicitly excluded, and is not explicitly included, the method returns an // OptionalBool with a false value. // // If the user's status in the segment is undefined, the method returns OptionalBool{} with no // value (so calling IsDefined() on it will return false). CheckMembership(segmentRef string) ldvalue.OptionalBool }