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>
143 lines
4.0 KiB
Go
143 lines
4.0 KiB
Go
package grammar
|
|
|
|
import (
|
|
"github.com/splitio/go-split-commons/v7/dtos"
|
|
"github.com/splitio/go-split-commons/v7/engine/grammar/datatypes"
|
|
"github.com/splitio/go-toolkit/v5/logging"
|
|
)
|
|
|
|
// Condition struct with added logic that wraps around a DTO
|
|
type Condition struct {
|
|
matchers []MatcherInterface
|
|
combiner string
|
|
partitions []Partition
|
|
label string
|
|
conditionType string
|
|
}
|
|
|
|
// NewCondition instantiates a new Condition struct with appropriate wrappers around dtos and returns it.
|
|
func NewCondition(cond *dtos.ConditionDTO, logger logging.LoggerInterface, ruleBuilder RuleBuilder) (*Condition, error) {
|
|
partitions := make([]Partition, 0)
|
|
for _, part := range cond.Partitions {
|
|
partitions = append(partitions, Partition{PartitionData: part})
|
|
}
|
|
matcherObjs, err := processMatchers(cond.MatcherGroup.Matchers, logger, ruleBuilder)
|
|
if err != nil {
|
|
// At this point the only error forwarded is UnsupportedMatcherError
|
|
return nil, err
|
|
}
|
|
|
|
return &Condition{
|
|
combiner: cond.MatcherGroup.Combiner,
|
|
matchers: matcherObjs,
|
|
partitions: partitions,
|
|
label: cond.Label,
|
|
conditionType: cond.ConditionType,
|
|
}, nil
|
|
}
|
|
|
|
func NewRBCondition(cond *dtos.RuleBasedConditionDTO, logger logging.LoggerInterface, ruleBuilder RuleBuilder) (*Condition, error) {
|
|
partitions := make([]Partition, 0)
|
|
matcherObjs, err := processMatchers(cond.MatcherGroup.Matchers, logger, ruleBuilder)
|
|
if err != nil {
|
|
// At this point the only error forwarded is UnsupportedMatcherError
|
|
return nil, err
|
|
}
|
|
|
|
return &Condition{
|
|
combiner: cond.MatcherGroup.Combiner,
|
|
matchers: matcherObjs,
|
|
partitions: partitions,
|
|
conditionType: cond.ConditionType,
|
|
}, nil
|
|
}
|
|
|
|
func processMatchers(condMatchers []dtos.MatcherDTO, logger logging.LoggerInterface, ruleBuilder RuleBuilder) ([]MatcherInterface, error) {
|
|
matcherObjs := make([]MatcherInterface, 0)
|
|
for _, matcher := range condMatchers {
|
|
m, err := ruleBuilder.BuildMatcher(&matcher)
|
|
if err == nil {
|
|
matcherObjs = append(matcherObjs, m)
|
|
} else {
|
|
logger.Debug("error in BuildMatcher, reason: ", err.Error())
|
|
if _, ok := err.(datatypes.UnsupportedMatcherError); ok {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
return matcherObjs, nil
|
|
}
|
|
|
|
// Partition struct with added logic that wraps around a DTO
|
|
type Partition struct {
|
|
PartitionData dtos.PartitionDTO
|
|
}
|
|
|
|
// ConditionType returns validated condition type. Whitelist by default
|
|
func (c *Condition) ConditionType() string {
|
|
switch c.conditionType {
|
|
case ConditionTypeRollout:
|
|
return ConditionTypeRollout
|
|
case ConditionTypeWhitelist:
|
|
return ConditionTypeWhitelist
|
|
default:
|
|
return ConditionTypeWhitelist
|
|
}
|
|
}
|
|
|
|
func (c *Condition) Combiner() string {
|
|
return c.combiner
|
|
}
|
|
|
|
// Label returns the condition's label
|
|
func (c *Condition) Label() string {
|
|
return c.label
|
|
}
|
|
|
|
// Matches returns true if the condition matches for a specific key and/or set of attributes
|
|
func (c *Condition) Matches(key string, bucketingKey *string, attributes map[string]interface{}) bool {
|
|
partial := make([]bool, len(c.matchers))
|
|
for i, matcher := range c.matchers {
|
|
partial[i] = matcher.Match(key, attributes, bucketingKey)
|
|
if matcher.Negate() {
|
|
partial[i] = !partial[i]
|
|
}
|
|
}
|
|
return applyCombiner(partial, c.combiner)
|
|
}
|
|
|
|
// CalculateTreatment calulates the treatment for a specific condition based on the bucket
|
|
func (c *Condition) CalculateTreatment(bucket int) *string {
|
|
accum := 0
|
|
for _, partition := range c.partitions {
|
|
accum += partition.PartitionData.Size
|
|
if bucket <= accum {
|
|
return &partition.PartitionData.Treatment
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func BuildCondition(conditionType string, label string, partitions []Partition, matchers []MatcherInterface, combiner string) *Condition {
|
|
return &Condition{
|
|
conditionType: conditionType,
|
|
label: label,
|
|
partitions: partitions,
|
|
matchers: matchers,
|
|
combiner: combiner,
|
|
}
|
|
}
|
|
|
|
func applyCombiner(results []bool, combiner string) bool {
|
|
temp := true
|
|
switch combiner {
|
|
case "AND":
|
|
for _, result := range results {
|
|
temp = temp && result
|
|
}
|
|
default:
|
|
return false
|
|
}
|
|
return temp
|
|
}
|