Skip to content

Commit 880c9e8

Browse files
authored
Merge pull request #247 from splitio/FME-11508
[FME-11508] Added evaluation options for treatments
2 parents 9320e13 + dd17f1c commit 880c9e8

File tree

4 files changed

+429
-140
lines changed

4 files changed

+429
-140
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/splitio/go-client/v6
33
go 1.18
44

55
require (
6-
github.com/splitio/go-split-commons/v9 v9.0.0
6+
github.com/splitio/go-split-commons/v9 v9.0.1-0.20251202184315-69a1a4a8c150
77
github.com/splitio/go-toolkit/v5 v5.4.1
88
github.com/stretchr/testify v1.11.1
99
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
2121
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2222
github.com/redis/go-redis/v9 v9.0.4 h1:FC82T+CHJ/Q/PdyLW++GeCO+Ol59Y4T7R4jbgjvktgc=
2323
github.com/redis/go-redis/v9 v9.0.4/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
24-
github.com/splitio/go-split-commons/v9 v9.0.0 h1:6uHEkBMUUZNhIiop9dyN04gXQUrMXp+X/0uXSytbp+Q=
25-
github.com/splitio/go-split-commons/v9 v9.0.0/go.mod h1:gJuaKo04Swlh4w9C1b2jBAqAdFxEd/Vpd8jnFINOeDY=
24+
github.com/splitio/go-split-commons/v9 v9.0.1-0.20251202184315-69a1a4a8c150 h1:rfNMXpXAKXzqM76WNuh+IKNGZ1LBwlyYXAx84tZMw80=
25+
github.com/splitio/go-split-commons/v9 v9.0.1-0.20251202184315-69a1a4a8c150/go.mod h1:gJuaKo04Swlh4w9C1b2jBAqAdFxEd/Vpd8jnFINOeDY=
2626
github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE=
2727
github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw=
2828
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=

splitio/client/client.go

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"encoding/json"
45
"errors"
56
"fmt"
67
"runtime/debug"
@@ -54,6 +55,22 @@ type TreatmentResult struct {
5455
Config *string `json:"config"`
5556
}
5657

58+
type options struct {
59+
evaluationOptions *dtos.EvaluationOptions
60+
}
61+
62+
type OptFn = func(o *options)
63+
64+
func (c *SplitClient) WithEvaluationOptions(e *dtos.EvaluationOptions) OptFn {
65+
return func(o *options) { o.evaluationOptions = e }
66+
}
67+
68+
func defaultOpts() options {
69+
return options{
70+
evaluationOptions: nil,
71+
}
72+
}
73+
5774
// getEvaluationResult calls evaluation for one particular feature flag
5875
func (c *SplitClient) getEvaluationResult(matchingKey string, bucketingKey *string, featureFlag string, attributes map[string]interface{}, operation string) *evaluator.Result {
5976
if c.isReady() {
@@ -95,7 +112,7 @@ func (c *SplitClient) getEvaluationsResult(matchingKey string, bucketingKey *str
95112
}
96113

97114
// createImpression creates impression to be stored and used by listener
98-
func (c *SplitClient) createImpression(featureFlag string, bucketingKey *string, evaluationLabel string, matchingKey string, treatment string, changeNumber int64, disabled bool) dtos.Impression {
115+
func (c *SplitClient) createImpression(featureFlag string, bucketingKey *string, evaluationLabel string, matchingKey string, treatment string, changeNumber int64, disabled bool, properties string) dtos.Impression {
99116
var label string
100117
if c.factory.cfg.LabelsEnabled {
101118
label = evaluationLabel
@@ -115,6 +132,7 @@ func (c *SplitClient) createImpression(featureFlag string, bucketingKey *string,
115132
Treatment: treatment,
116133
Time: time.Now().UTC().UnixNano() / int64(time.Millisecond), // Convert standard timestamp to java's ms timestamps
117134
Disabled: disabled,
135+
Properties: properties,
118136
}
119137
}
120138

@@ -140,7 +158,7 @@ func (c *SplitClient) storeData(impressions []dtos.Impression, attributes map[st
140158
}
141159

142160
// doTreatmentCall retrieves treatments of an specific feature flag with configurations object if it is present for a certain key and set of attributes
143-
func (c *SplitClient) doTreatmentCall(key interface{}, featureFlag string, attributes map[string]interface{}, operation string, metricsLabel string) (t TreatmentResult) {
161+
func (c *SplitClient) doTreatmentCall(key interface{}, featureFlag string, attributes map[string]interface{}, operation string, metricsLabel string, evaluationOptions *dtos.EvaluationOptions) (t TreatmentResult) {
144162
controlTreatment := TreatmentResult{
145163
Treatment: evaluator.Control,
146164
Config: nil,
@@ -184,7 +202,7 @@ func (c *SplitClient) doTreatmentCall(key interface{}, featureFlag string, attri
184202
}
185203

186204
c.storeData(
187-
[]dtos.Impression{c.createImpression(featureFlag, bucketingKey, evaluationResult.Label, matchingKey, evaluationResult.Treatment, evaluationResult.SplitChangeNumber, evaluationResult.ImpressionsDisabled)},
205+
[]dtos.Impression{c.createImpression(featureFlag, bucketingKey, evaluationResult.Label, matchingKey, evaluationResult.Treatment, evaluationResult.SplitChangeNumber, evaluationResult.ImpressionsDisabled, serializeProperties(evaluationOptions))},
188206
attributes,
189207
metricsLabel,
190208
evaluationResult.EvaluationTime,
@@ -196,16 +214,42 @@ func (c *SplitClient) doTreatmentCall(key interface{}, featureFlag string, attri
196214
}
197215
}
198216

217+
func serializeProperties(opts *dtos.EvaluationOptions) string {
218+
if opts == nil {
219+
return ""
220+
}
221+
if len(opts.Properties) == 0 {
222+
return ""
223+
}
224+
225+
properties, err := json.Marshal(opts.Properties)
226+
if err != nil {
227+
return ""
228+
}
229+
230+
return string(properties)
231+
}
232+
199233
// Treatment implements the main functionality of split. Retrieve treatments of a specific feature flag
200234
// for a certain key and set of attributes
201-
func (c *SplitClient) Treatment(key interface{}, featureFlagName string, attributes map[string]interface{}) string {
202-
return c.doTreatmentCall(key, featureFlagName, attributes, treatment, telemetry.Treatment).Treatment
235+
func (c *SplitClient) Treatment(key interface{}, featureFlagName string, attributes map[string]interface{}, optFns ...OptFn) string {
236+
options := getOptions(optFns...)
237+
return c.doTreatmentCall(key, featureFlagName, attributes, treatment, telemetry.Treatment, options.evaluationOptions).Treatment
238+
}
239+
240+
func getOptions(optFns ...OptFn) options {
241+
options := defaultOpts()
242+
for _, optFn := range optFns {
243+
optFn(&options)
244+
}
245+
return options
203246
}
204247

205248
// TreatmentWithConfig implements the main functionality of split. Retrieves the treatment of a specific feature flag
206249
// with the corresponding configuration if it is present
207-
func (c *SplitClient) TreatmentWithConfig(key interface{}, featureFlagName string, attributes map[string]interface{}) TreatmentResult {
208-
return c.doTreatmentCall(key, featureFlagName, attributes, treatmentWithConfig, telemetry.TreatmentWithConfig)
250+
func (c *SplitClient) TreatmentWithConfig(key interface{}, featureFlagName string, attributes map[string]interface{}, optFns ...OptFn) TreatmentResult {
251+
options := getOptions(optFns...)
252+
return c.doTreatmentCall(key, featureFlagName, attributes, treatmentWithConfig, telemetry.TreatmentWithConfig, options.evaluationOptions)
209253
}
210254

211255
// Generates control treatments
@@ -224,7 +268,7 @@ func (c *SplitClient) generateControlTreatments(featureFlagNames []string, opera
224268
return treatments
225269
}
226270

227-
func (c *SplitClient) processResult(result evaluator.Results, operation string, bucketingKey *string, matchingKey string, attributes map[string]interface{}, metricsLabel string) (t map[string]TreatmentResult) {
271+
func (c *SplitClient) processResult(result evaluator.Results, operation string, bucketingKey *string, matchingKey string, attributes map[string]interface{}, metricsLabel string, evaluationOptions *dtos.EvaluationOptions) (t map[string]TreatmentResult) {
228272
var bulkImpressions []dtos.Impression
229273
treatments := make(map[string]TreatmentResult)
230274
for feature, evaluation := range result.Evaluations {
@@ -234,7 +278,8 @@ func (c *SplitClient) processResult(result evaluator.Results, operation string,
234278
Config: nil,
235279
}
236280
} else {
237-
bulkImpressions = append(bulkImpressions, c.createImpression(feature, bucketingKey, evaluation.Label, matchingKey, evaluation.Treatment, evaluation.SplitChangeNumber, evaluation.ImpressionsDisabled))
281+
bulkImpressions = append(bulkImpressions, c.createImpression(feature, bucketingKey, evaluation.Label, matchingKey, evaluation.Treatment, evaluation.SplitChangeNumber, evaluation.ImpressionsDisabled, serializeProperties(evaluationOptions)))
282+
//bulkImpressions = append(bulkImpressions, c.createImpression(feature, bucketingKey, evaluation.Label, matchingKey, evaluation.Treatment, evaluation.SplitChangeNumber, evaluation.ImpressionsDisabled, ""))
238283

239284
treatments[feature] = TreatmentResult{
240285
Treatment: evaluation.Treatment,
@@ -247,7 +292,7 @@ func (c *SplitClient) processResult(result evaluator.Results, operation string,
247292
}
248293

249294
// doTreatmentsCall retrieves treatments of an specific array of feature flag names with configurations object if it is present for a certain key and set of attributes
250-
func (c *SplitClient) doTreatmentsCall(key interface{}, featureFlagNames []string, attributes map[string]interface{}, operation string, metricsLabel string) (t map[string]TreatmentResult) {
295+
func (c *SplitClient) doTreatmentsCall(key interface{}, featureFlagNames []string, attributes map[string]interface{}, operation string, metricsLabel string, evaluationOptions *dtos.EvaluationOptions) (t map[string]TreatmentResult) {
251296
// Set up a guard deferred function to recover if the SDK starts panicking
252297
defer func() {
253298
if r := recover(); r != nil {
@@ -280,11 +325,11 @@ func (c *SplitClient) doTreatmentsCall(key interface{}, featureFlagNames []strin
280325

281326
evaluationsResult := c.getEvaluationsResult(matchingKey, bucketingKey, filteredFeatures, attributes, operation)
282327

283-
return c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel)
328+
return c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel, evaluationOptions)
284329
}
285330

286331
// doTreatmentsCallByFlagSets retrieves treatments of a specific array of feature flag names, that belong to flag sets, with configurations object if it is present for a certain key and set of attributes
287-
func (c *SplitClient) doTreatmentsCallByFlagSets(key interface{}, flagSets []string, attributes map[string]interface{}, operation string, metricsLabel string) (t map[string]TreatmentResult) {
332+
func (c *SplitClient) doTreatmentsCallByFlagSets(key interface{}, flagSets []string, attributes map[string]interface{}, operation string, metricsLabel string, evaluationOptions *dtos.EvaluationOptions) (t map[string]TreatmentResult) {
288333
treatments := make(map[string]TreatmentResult)
289334

290335
// Set up a guard deferred function to recover if the SDK starts panicking
@@ -312,15 +357,16 @@ func (c *SplitClient) doTreatmentsCallByFlagSets(key interface{}, flagSets []str
312357

313358
if c.isReady() {
314359
evaluationsResult := c.evaluator.EvaluateFeatureByFlagSets(matchingKey, bucketingKey, flagSets, attributes)
315-
treatments = c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel)
360+
treatments = c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel, evaluationOptions)
316361
}
317362
return treatments
318363
}
319364

320365
// Treatments evaluates multiple feature flag names for a single user and set of attributes at once
321-
func (c *SplitClient) Treatments(key interface{}, featureFlagNames []string, attributes map[string]interface{}) map[string]string {
366+
func (c *SplitClient) Treatments(key interface{}, featureFlagNames []string, attributes map[string]interface{}, optFns ...OptFn) map[string]string {
367+
options := getOptions(optFns...)
322368
treatmentsResult := map[string]string{}
323-
result := c.doTreatmentsCall(key, featureFlagNames, attributes, treatments, telemetry.Treatments)
369+
result := c.doTreatmentsCall(key, featureFlagNames, attributes, treatments, telemetry.Treatments, options.evaluationOptions)
324370
for feature, treatmentResult := range result {
325371
treatmentsResult[feature] = treatmentResult.Treatment
326372
}
@@ -348,27 +394,29 @@ func (c *SplitClient) validateSets(flagSets []string) []string {
348394
}
349395

350396
// Treatments evaluate multiple feature flag names belonging to a flag set for a single user and a set of attributes at once
351-
func (c *SplitClient) TreatmentsByFlagSet(key interface{}, flagSet string, attributes map[string]interface{}) map[string]string {
397+
func (c *SplitClient) TreatmentsByFlagSet(key interface{}, flagSet string, attributes map[string]interface{}, optFns ...OptFn) map[string]string {
398+
options := getOptions(optFns...)
352399
treatmentsResult := map[string]string{}
353400
sets := c.validateSets([]string{flagSet})
354401
if sets == nil {
355402
return treatmentsResult
356403
}
357-
result := c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsByFlagSet, telemetry.TreatmentsByFlagSet)
404+
result := c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsByFlagSet, telemetry.TreatmentsByFlagSet, options.evaluationOptions)
358405
for feature, treatmentResult := range result {
359406
treatmentsResult[feature] = treatmentResult.Treatment
360407
}
361408
return treatmentsResult
362409
}
363410

364411
// Treatments evaluate multiple feature flag names belonging to flag sets for a single user and a set of attributes at once
365-
func (c *SplitClient) TreatmentsByFlagSets(key interface{}, flagSets []string, attributes map[string]interface{}) map[string]string {
412+
func (c *SplitClient) TreatmentsByFlagSets(key interface{}, flagSets []string, attributes map[string]interface{}, optFns ...OptFn) map[string]string {
413+
options := getOptions(optFns...)
366414
treatmentsResult := map[string]string{}
367415
flagSets = c.validateSets(flagSets)
368416
if flagSets == nil {
369417
return treatmentsResult
370418
}
371-
result := c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsByFlagSets, telemetry.TreatmentsByFlagSets)
419+
result := c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsByFlagSets, telemetry.TreatmentsByFlagSets, options.evaluationOptions)
372420
for feature, treatmentResult := range result {
373421
treatmentsResult[feature] = treatmentResult.Treatment
374422
}
@@ -388,28 +436,31 @@ func (c *SplitClient) filterSetsAreInConfig(flagSets []string) []string {
388436
}
389437

390438
// TreatmentsWithConfig evaluates multiple feature flag names for a single user and set of attributes at once and returns configurations
391-
func (c *SplitClient) TreatmentsWithConfig(key interface{}, featureFlagNames []string, attributes map[string]interface{}) map[string]TreatmentResult {
392-
return c.doTreatmentsCall(key, featureFlagNames, attributes, treatmentsWithConfig, telemetry.TreatmentsWithConfig)
439+
func (c *SplitClient) TreatmentsWithConfig(key interface{}, featureFlagNames []string, attributes map[string]interface{}, optFns ...OptFn) map[string]TreatmentResult {
440+
options := getOptions(optFns...)
441+
return c.doTreatmentsCall(key, featureFlagNames, attributes, treatmentsWithConfig, telemetry.TreatmentsWithConfig, options.evaluationOptions)
393442
}
394443

395444
// TreatmentsWithConfigByFlagSet evaluates multiple feature flag names belonging to a flag set for a single user and set of attributes at once and returns configurations
396-
func (c *SplitClient) TreatmentsWithConfigByFlagSet(key interface{}, flagSet string, attributes map[string]interface{}) map[string]TreatmentResult {
445+
func (c *SplitClient) TreatmentsWithConfigByFlagSet(key interface{}, flagSet string, attributes map[string]interface{}, optFns ...OptFn) map[string]TreatmentResult {
446+
options := getOptions(optFns...)
397447
treatmentsResult := make(map[string]TreatmentResult)
398448
sets := c.validateSets([]string{flagSet})
399449
if sets == nil {
400450
return treatmentsResult
401451
}
402-
return c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsWithConfigByFlagSet, telemetry.TreatmentsWithConfigByFlagSet)
452+
return c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsWithConfigByFlagSet, telemetry.TreatmentsWithConfigByFlagSet, options.evaluationOptions)
403453
}
404454

405455
// TreatmentsWithConfigByFlagSet evaluates multiple feature flag names belonging to a flag sets for a single user and set of attributes at once and returns configurations
406-
func (c *SplitClient) TreatmentsWithConfigByFlagSets(key interface{}, flagSets []string, attributes map[string]interface{}) map[string]TreatmentResult {
456+
func (c *SplitClient) TreatmentsWithConfigByFlagSets(key interface{}, flagSets []string, attributes map[string]interface{}, optFns ...OptFn) map[string]TreatmentResult {
457+
options := getOptions(optFns...)
407458
treatmentsResult := make(map[string]TreatmentResult)
408459
flagSets = c.validateSets(flagSets)
409460
if flagSets == nil {
410461
return treatmentsResult
411462
}
412-
return c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsWithConfigByFlagSets, telemetry.TreatmentsWithConfigByFlagSets)
463+
return c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsWithConfigByFlagSets, telemetry.TreatmentsWithConfigByFlagSets, options.evaluationOptions)
413464
}
414465

415466
// isDestroyed returns true if the client has been destroyed

0 commit comments

Comments
 (0)