Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
74 lines
2.2 KiB
Go
74 lines
2.2 KiB
Go
package engine
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/splitio/go-split-commons/v7/engine/evaluator/impressionlabels"
|
|
"github.com/splitio/go-split-commons/v7/engine/grammar"
|
|
"github.com/splitio/go-split-commons/v7/engine/grammar/constants"
|
|
"github.com/splitio/go-split-commons/v7/engine/hash"
|
|
|
|
"github.com/splitio/go-toolkit/v5/hasher"
|
|
"github.com/splitio/go-toolkit/v5/logging"
|
|
)
|
|
|
|
// Engine struct is responsible for checking if any of the conditions of the feature flag matches,
|
|
// performing traffic allocation, calculating the bucket and returning the appropriate treatment
|
|
type Engine struct {
|
|
logger logging.LoggerInterface
|
|
}
|
|
|
|
// DoEvaluation performs the main evaluation against each condition
|
|
func (e *Engine) DoEvaluation(
|
|
split *grammar.Split,
|
|
key string,
|
|
bucketingKey string,
|
|
attributes map[string]interface{},
|
|
) (*string, string) {
|
|
inRollOut := false
|
|
for _, cond := range split.Conditions() {
|
|
if !inRollOut && cond.ConditionType() == grammar.ConditionTypeRollout {
|
|
if split.TrafficAllocation() < 100 {
|
|
bucket := e.calculateBucket(split.Algo(), bucketingKey, split.TrafficAllocationSeed())
|
|
if bucket > split.TrafficAllocation() {
|
|
e.logger.Debug(fmt.Sprintf(
|
|
"Traffic allocation exceeded for feature %s and key %s."+
|
|
" Returning default treatment", split.Name(), key,
|
|
))
|
|
defaultTreatment := split.DefaultTreatment()
|
|
return &defaultTreatment, impressionlabels.NotInSplit
|
|
}
|
|
inRollOut = true
|
|
}
|
|
}
|
|
|
|
if cond.Matches(key, &bucketingKey, attributes) {
|
|
bucket := e.calculateBucket(split.Algo(), bucketingKey, split.Seed())
|
|
treatment := cond.CalculateTreatment(bucket)
|
|
return treatment, cond.Label()
|
|
}
|
|
}
|
|
return nil, impressionlabels.NoConditionMatched
|
|
}
|
|
|
|
func (e *Engine) calculateBucket(algo int, bucketingKey string, seed int64) int {
|
|
var hashedKey uint32
|
|
switch algo {
|
|
case constants.SplitAlgoMurmur:
|
|
hashedKey = hasher.Sum32WithSeed([]byte(bucketingKey), uint32(seed))
|
|
case constants.SplitAlgoLegacy:
|
|
fallthrough
|
|
default:
|
|
hashedKey = hash.Legacy([]byte(bucketingKey), uint32(seed))
|
|
}
|
|
|
|
return int(math.Abs(float64(hashedKey%100)) + 1)
|
|
|
|
}
|
|
|
|
// NewEngine instantiates and returns a new engine
|
|
func NewEngine(logger logging.LoggerInterface) *Engine {
|
|
return &Engine{logger: logger}
|
|
}
|