1 package evaluation 2 3 import ( 4 "github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel" 5 6 "github.com/launchdarkly/go-sdk-common/v3/ldcontext" 7 "github.com/launchdarkly/go-sdk-common/v3/ldreason" 8 "github.com/launchdarkly/go-sdk-common/v3/ldvalue" 9 ) 10 11 // Evaluator is the engine for evaluating feature flags. 12 type Evaluator interface { 13 // Evaluate evaluates a feature flag for the specified context. 14 // 15 // The flag is passed by reference only for efficiency; the evaluator will never modify any flag 16 // properties. Passing a nil flag will result in a panic. 17 // 18 // The evaluator does not know anything about analytics events; generating any appropriate analytics 19 // events is the responsibility of the caller, who can also provide a callback in prerequisiteFlagEventRecorder 20 // to be notified if any additional evaluations were done due to prerequisites. The prerequisiteFlagEventRecorder 21 // parameter can be nil if you do not need to track prerequisite evaluations. 22 Evaluate( 23 flag *ldmodel.FeatureFlag, 24 context ldcontext.Context, 25 prerequisiteFlagEventRecorder PrerequisiteFlagEventRecorder, 26 ) Result 27 } 28 29 // PrerequisiteFlagEventRecorder is a function that Evaluator.Evaluate() will call to record the 30 // result of a prerequisite flag evaluation. 31 type PrerequisiteFlagEventRecorder func(PrerequisiteFlagEvent) 32 33 // PrerequisiteFlagEvent is the parameter data passed to PrerequisiteFlagEventRecorder. 34 type PrerequisiteFlagEvent struct { 35 // TargetFlagKey is the key of the feature flag that had a prerequisite. 36 TargetFlagKey string 37 // Context is the context that the flag was evaluated for. We pass this back to the caller, even though the caller 38 // already passed it to us in the Evaluate parameters, so that the caller can provide a stateless function for 39 // PrerequisiteFlagEventRecorder rather than a closure (since closures are less efficient). 40 Context ldcontext.Context 41 // PrerequisiteFlag is the full configuration of the prerequisite flag. We need to pass the full flag here rather 42 // than just the key because the flag's properties (such as TrackEvents) can affect how events are generated. 43 // This is passed by reference for efficiency only, and will never be nil; the PrerequisiteFlagEventRecorder 44 // must not modify the flag's properties. 45 PrerequisiteFlag *ldmodel.FeatureFlag 46 // PrerequisiteResult is the result of evaluating the prerequisite flag. 47 PrerequisiteResult Result 48 } 49 50 // DataProvider is an abstraction for querying feature flags and user segments from a data store. 51 // The caller provides an implementation of this interface to NewEvaluator. 52 // 53 // Flags and segments are returned by reference for efficiency only (on the assumption that the 54 // caller already has these objects in memory); the evaluator will never modify their properties. 55 type DataProvider interface { 56 // GetFeatureFlag attempts to retrieve a feature flag from the data store by key. 57 // 58 // The evaluator calls this method if a flag contains a prerequisite condition referencing 59 // another flag. 60 // 61 // The method returns nil if the flag was not found. The DataProvider should treat any deleted 62 // flag as "not found" even if the data store contains a deleted flag placeholder for it. 63 GetFeatureFlag(key string) *ldmodel.FeatureFlag 64 // GetSegment attempts to retrieve a user segment from the data store by key. 65 // 66 // The evaluator calls this method if a clause in a flag rule uses the OperatorSegmentMatch 67 // test. 68 // 69 // The method returns nil if the segment was not found. The DataProvider should treat any deleted 70 // segment as "not found" even if the data store contains a deleted segment placeholder for it. 71 GetSegment(key string) *ldmodel.Segment 72 } 73 74 // BigSegmentProvider is an abstraction for querying membership in big segments. The caller 75 // provides an implementation of this interface to NewEvaluatorWithBigSegments. 76 type BigSegmentProvider interface { 77 // GetMembership queries a snapshot of the current segment state for a specific context 78 // key. 79 // 80 // The underlying big segment store implementation will use a hash of the context key, rather 81 // than the raw key. But computing the hash is the responsibility of the BigSegmentProvider 82 // implementation rather than the evaluator, because there may already have a cached result for 83 // that user, and we don't want to have to compute a hash repeatedly just to query a cache. 84 // 85 // Any given big segment is specific to one context kind, so we do not specify a context kind 86 // here; it is OK for the membership results to include different context kinds for the same 87 // key. That is, if for instance the context {kind: "user", key: "x"} is included in big segment 88 // S1, and the context {kind: "org", key: "x"} is included in big segment S2, then the query 89 // result for key "x" will show that it is included in both S1 and S2; even though those "x" 90 // keys are really for two unrelated context kinds, we will always know which kind we mean if 91 // we are specifically checking either S1 or S2, because S1 is defined as only applying to the 92 // "user" kind and S2 is defined as only applying to the "org" kind. 93 // 94 // If the returned BigSegmentMembership is nil, it is treated the same as an implementation 95 // whose CheckMembership method always returns an empty value. 96 GetMembership( 97 contextKey string, 98 ) (BigSegmentMembership, ldreason.BigSegmentsStatus) 99 } 100 101 // BigSegmentMembership is the return type of BigSegmentProvider.GetContextMembership(). It is 102 // associated with a single context kind and context key, and provides the ability to check whether 103 // that context is included in or excluded from any number of big segments. 104 // 105 // This is an immutable snapshot of the state for this context at the time GetBigSegmentMembership 106 // was called. Calling CheckMembership should not cause the state to be queried again. The object 107 // should be safe for concurrent access by multiple goroutines. 108 // 109 // This interface also exists in go-server-sdk because it is exposed as part of the public SDK API; 110 // users can write their own implementations of SDK components, but we do not want application code 111 // to reference go-server-sdk-evaluation symbols directly as part of that, because this library is 112 // versioned separately from the SDK. Currently the two interfaces are identical, but it might be 113 // that the go-server-sdk-evaluation version would diverge from the go-server-sdk version due to 114 // some internal requirements that aren't relevant to users, in which case go-server-sdk would be 115 // responsible for bridging the difference. 116 type BigSegmentMembership interface { 117 // CheckMembership tests whether the user is explicitly included or explicitly excluded in the 118 // specified segment, or neither. The segment is identified by a segmentRef which is not the 119 // same as the segment key-- it includes the key but also versioning information that the SDK 120 // will provide. The store implementation should not be concerned with the format of this. 121 // 122 // If the user is explicitly included (regardless of whether the user is also explicitly 123 // excluded or not-- that is, inclusion takes priority over exclusion), the method returns an 124 // OptionalBool with a true value. 125 // 126 // If the user is explicitly excluded, and is not explicitly included, the method returns an 127 // OptionalBool with a false value. 128 // 129 // If the user's status in the segment is undefined, the method returns OptionalBool{} with no 130 // value (so calling IsDefined() on it will return false). 131 CheckMembership(segmentRef string) ldvalue.OptionalBool 132 } 133