Skip to content

Commit a70de54

Browse files
committed
Added evaluation options for treatments
1 parent 9320e13 commit a70de54

File tree

4 files changed

+480
-140
lines changed

4 files changed

+480
-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: 127 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"
@@ -22,14 +23,22 @@ import (
2223
)
2324

2425
const (
25-
treatment = "Treatment"
26-
treatments = "Treatments"
27-
treatmentsByFlagSet = "TreatmentsByFlagSet"
28-
treatmentsByFlagSets = "TreatmentsByFlagSets"
29-
treatmentWithConfig = "TreatmentWithConfig"
30-
treatmentsWithConfig = "TreatmentsWithConfig"
31-
treatmentsWithConfigByFlagSet = "TreatmentsWithConfigByFlagSet"
32-
treatmentsWithConfigByFlagSets = "TreatmentsWithConfigByFlagSets"
26+
treatment = "Treatment"
27+
treatments = "Treatments"
28+
treatmentsByFlagSet = "TreatmentsByFlagSet"
29+
treatmentsByFlagSets = "TreatmentsByFlagSets"
30+
treatmentWithConfig = "TreatmentWithConfig"
31+
treatmentsWithConfig = "TreatmentsWithConfig"
32+
treatmentsWithConfigByFlagSet = "TreatmentsWithConfigByFlagSet"
33+
treatmentsWithConfigByFlagSets = "TreatmentsWithConfigByFlagSets"
34+
treatmentWithEvaluationOptions = "TreatmentWithEvaluationOptions"
35+
treatmentsWithEvaluationOptions = "TreatmentsWithEvaluationOptions"
36+
treatmentsByFlagSetWithEvaluationOptions = "TreatmentsByFlagSetWithEvaluationOptions"
37+
treatmentsByFlagSetsWithEvaluationOptions = "TreatmentsByFlagSetsWithEvaluationOptions"
38+
treatmentWithConfigAndEvaluationOptions = "TreatmentWithConfigWithEvaluationOptions"
39+
treatmentsWithConfigAndEvaluationOptions = "TreatmentsWithConfigWithEvaluationOptions"
40+
treatmentsWithConfigByFlagSetAndEvaluationOptions = "TreatmentsWithConfigByFlagSetWithEvaluationOptions"
41+
treatmentsWithConfigByFlagSetsAndEvaluationOptions = "TreatmentsWithConfigByFlagSetsWithEvaluationOptions"
3342
)
3443

3544
// SplitClient is the entry-point of the split SDK.
@@ -95,7 +104,7 @@ func (c *SplitClient) getEvaluationsResult(matchingKey string, bucketingKey *str
95104
}
96105

97106
// 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 {
107+
func (c *SplitClient) createImpression(featureFlag string, bucketingKey *string, evaluationLabel string, matchingKey string, treatment string, changeNumber int64, disabled bool, properties string) dtos.Impression {
99108
var label string
100109
if c.factory.cfg.LabelsEnabled {
101110
label = evaluationLabel
@@ -115,6 +124,7 @@ func (c *SplitClient) createImpression(featureFlag string, bucketingKey *string,
115124
Treatment: treatment,
116125
Time: time.Now().UTC().UnixNano() / int64(time.Millisecond), // Convert standard timestamp to java's ms timestamps
117126
Disabled: disabled,
127+
Properties: properties,
118128
}
119129
}
120130

@@ -140,7 +150,7 @@ func (c *SplitClient) storeData(impressions []dtos.Impression, attributes map[st
140150
}
141151

142152
// 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) {
153+
func (c *SplitClient) doTreatmentCall(key interface{}, featureFlag string, attributes map[string]interface{}, operation string, metricsLabel string, evaluationOptions *dtos.EvaluationOptions) (t TreatmentResult) {
144154
controlTreatment := TreatmentResult{
145155
Treatment: evaluator.Control,
146156
Config: nil,
@@ -184,7 +194,7 @@ func (c *SplitClient) doTreatmentCall(key interface{}, featureFlag string, attri
184194
}
185195

186196
c.storeData(
187-
[]dtos.Impression{c.createImpression(featureFlag, bucketingKey, evaluationResult.Label, matchingKey, evaluationResult.Treatment, evaluationResult.SplitChangeNumber, evaluationResult.ImpressionsDisabled)},
197+
[]dtos.Impression{c.createImpression(featureFlag, bucketingKey, evaluationResult.Label, matchingKey, evaluationResult.Treatment, evaluationResult.SplitChangeNumber, evaluationResult.ImpressionsDisabled, serializeProperties(evaluationOptions))},
188198
attributes,
189199
metricsLabel,
190200
evaluationResult.EvaluationTime,
@@ -196,16 +206,44 @@ func (c *SplitClient) doTreatmentCall(key interface{}, featureFlag string, attri
196206
}
197207
}
198208

209+
func serializeProperties(opts *dtos.EvaluationOptions) string {
210+
if opts == nil {
211+
return ""
212+
}
213+
if len(opts.Properties) == 0 {
214+
return ""
215+
}
216+
217+
properties, err := json.Marshal(opts.Properties)
218+
if err != nil {
219+
return ""
220+
}
221+
222+
return string(properties)
223+
}
224+
199225
// Treatment implements the main functionality of split. Retrieve treatments of a specific feature flag
200226
// for a certain key and set of attributes
201227
func (c *SplitClient) Treatment(key interface{}, featureFlagName string, attributes map[string]interface{}) string {
202-
return c.doTreatmentCall(key, featureFlagName, attributes, treatment, telemetry.Treatment).Treatment
228+
return c.doTreatmentCall(key, featureFlagName, attributes, treatment, telemetry.Treatment, nil).Treatment
203229
}
204230

205231
// TreatmentWithConfig implements the main functionality of split. Retrieves the treatment of a specific feature flag
206232
// with the corresponding configuration if it is present
207233
func (c *SplitClient) TreatmentWithConfig(key interface{}, featureFlagName string, attributes map[string]interface{}) TreatmentResult {
208-
return c.doTreatmentCall(key, featureFlagName, attributes, treatmentWithConfig, telemetry.TreatmentWithConfig)
234+
return c.doTreatmentCall(key, featureFlagName, attributes, treatmentWithConfig, telemetry.TreatmentWithConfig, nil)
235+
}
236+
237+
// TreatmentWithEvaluationOptions implements the main functionality of split. Retrieve treatments of a specific feature flag
238+
// for a certain key and set of attributes
239+
func (c *SplitClient) TreatmentWithEvaluationOptions(key interface{}, featureFlagName string, attributes map[string]interface{}, evaluationOptions dtos.EvaluationOptions) string {
240+
return c.doTreatmentCall(key, featureFlagName, attributes, treatmentWithEvaluationOptions, telemetry.TreatmentWithEvaluationOptions, &evaluationOptions).Treatment
241+
}
242+
243+
// TreatmentWithConfigAndEvaluationOptions implements the main functionality of split. Retrieves the treatment of a specific feature flag
244+
// with the corresponding configuration if it is present
245+
func (c *SplitClient) TreatmentWithConfigAndEvaluationOptions(key interface{}, featureFlagName string, attributes map[string]interface{}, evaluationOptions dtos.EvaluationOptions) TreatmentResult {
246+
return c.doTreatmentCall(key, featureFlagName, attributes, treatmentWithConfigAndEvaluationOptions, telemetry.TreatmentWithConfigAndEvaluationOptions, &evaluationOptions)
209247
}
210248

211249
// Generates control treatments
@@ -224,7 +262,7 @@ func (c *SplitClient) generateControlTreatments(featureFlagNames []string, opera
224262
return treatments
225263
}
226264

227-
func (c *SplitClient) processResult(result evaluator.Results, operation string, bucketingKey *string, matchingKey string, attributes map[string]interface{}, metricsLabel string) (t map[string]TreatmentResult) {
265+
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) {
228266
var bulkImpressions []dtos.Impression
229267
treatments := make(map[string]TreatmentResult)
230268
for feature, evaluation := range result.Evaluations {
@@ -234,7 +272,8 @@ func (c *SplitClient) processResult(result evaluator.Results, operation string,
234272
Config: nil,
235273
}
236274
} else {
237-
bulkImpressions = append(bulkImpressions, c.createImpression(feature, bucketingKey, evaluation.Label, matchingKey, evaluation.Treatment, evaluation.SplitChangeNumber, evaluation.ImpressionsDisabled))
275+
bulkImpressions = append(bulkImpressions, c.createImpression(feature, bucketingKey, evaluation.Label, matchingKey, evaluation.Treatment, evaluation.SplitChangeNumber, evaluation.ImpressionsDisabled, serializeProperties(evaluationOptions)))
276+
//bulkImpressions = append(bulkImpressions, c.createImpression(feature, bucketingKey, evaluation.Label, matchingKey, evaluation.Treatment, evaluation.SplitChangeNumber, evaluation.ImpressionsDisabled, ""))
238277

239278
treatments[feature] = TreatmentResult{
240279
Treatment: evaluation.Treatment,
@@ -247,7 +286,7 @@ func (c *SplitClient) processResult(result evaluator.Results, operation string,
247286
}
248287

249288
// 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) {
289+
func (c *SplitClient) doTreatmentsCall(key interface{}, featureFlagNames []string, attributes map[string]interface{}, operation string, metricsLabel string, evaluationOptions *dtos.EvaluationOptions) (t map[string]TreatmentResult) {
251290
// Set up a guard deferred function to recover if the SDK starts panicking
252291
defer func() {
253292
if r := recover(); r != nil {
@@ -280,11 +319,11 @@ func (c *SplitClient) doTreatmentsCall(key interface{}, featureFlagNames []strin
280319

281320
evaluationsResult := c.getEvaluationsResult(matchingKey, bucketingKey, filteredFeatures, attributes, operation)
282321

283-
return c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel)
322+
return c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel, evaluationOptions)
284323
}
285324

286325
// 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) {
326+
func (c *SplitClient) doTreatmentsCallByFlagSets(key interface{}, flagSets []string, attributes map[string]interface{}, operation string, metricsLabel string, evaluationOptions *dtos.EvaluationOptions) (t map[string]TreatmentResult) {
288327
treatments := make(map[string]TreatmentResult)
289328

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

313352
if c.isReady() {
314353
evaluationsResult := c.evaluator.EvaluateFeatureByFlagSets(matchingKey, bucketingKey, flagSets, attributes)
315-
treatments = c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel)
354+
treatments = c.processResult(evaluationsResult, operation, bucketingKey, matchingKey, attributes, metricsLabel, evaluationOptions)
316355
}
317356
return treatments
318357
}
319358

320359
// Treatments evaluates multiple feature flag names for a single user and set of attributes at once
321360
func (c *SplitClient) Treatments(key interface{}, featureFlagNames []string, attributes map[string]interface{}) map[string]string {
322361
treatmentsResult := map[string]string{}
323-
result := c.doTreatmentsCall(key, featureFlagNames, attributes, treatments, telemetry.Treatments)
362+
result := c.doTreatmentsCall(key, featureFlagNames, attributes, treatments, telemetry.Treatments, nil)
363+
for feature, treatmentResult := range result {
364+
treatmentsResult[feature] = treatmentResult.Treatment
365+
}
366+
return treatmentsResult
367+
}
368+
369+
// TreatmentsWithEvaluationOptions evaluates multiple feature flag names for a single user and set of attributes at once
370+
func (c *SplitClient) TreatmentsWithEvaluationOptions(key interface{}, featureFlagNames []string, attributes map[string]interface{}, evaluationOptions dtos.EvaluationOptions) map[string]string {
371+
treatmentsResult := map[string]string{}
372+
result := c.doTreatmentsCall(key, featureFlagNames, attributes, treatmentsWithEvaluationOptions, telemetry.TreatmentsWithEvaluationOptions, &evaluationOptions)
324373
for feature, treatmentResult := range result {
325374
treatmentsResult[feature] = treatmentResult.Treatment
326375
}
@@ -354,7 +403,7 @@ func (c *SplitClient) TreatmentsByFlagSet(key interface{}, flagSet string, attri
354403
if sets == nil {
355404
return treatmentsResult
356405
}
357-
result := c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsByFlagSet, telemetry.TreatmentsByFlagSet)
406+
result := c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsByFlagSet, telemetry.TreatmentsByFlagSet, nil)
358407
for feature, treatmentResult := range result {
359408
treatmentsResult[feature] = treatmentResult.Treatment
360409
}
@@ -368,7 +417,35 @@ func (c *SplitClient) TreatmentsByFlagSets(key interface{}, flagSets []string, a
368417
if flagSets == nil {
369418
return treatmentsResult
370419
}
371-
result := c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsByFlagSets, telemetry.TreatmentsByFlagSets)
420+
result := c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsByFlagSets, telemetry.TreatmentsByFlagSets, nil)
421+
for feature, treatmentResult := range result {
422+
treatmentsResult[feature] = treatmentResult.Treatment
423+
}
424+
return treatmentsResult
425+
}
426+
427+
// TreatmentsByFlagSetWithEvaluationOptions evaluate multiple feature flag names belonging to a flag set for a single user and a set of attributes at once
428+
func (c *SplitClient) TreatmentsByFlagSetWithEvaluationOptions(key interface{}, flagSet string, attributes map[string]interface{}, evaluationOptions dtos.EvaluationOptions) map[string]string {
429+
treatmentsResult := map[string]string{}
430+
sets := c.validateSets([]string{flagSet})
431+
if sets == nil {
432+
return treatmentsResult
433+
}
434+
result := c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsByFlagSetWithEvaluationOptions, telemetry.TreatmentsByFlagSetWithEvaluationOptions, &evaluationOptions)
435+
for feature, treatmentResult := range result {
436+
treatmentsResult[feature] = treatmentResult.Treatment
437+
}
438+
return treatmentsResult
439+
}
440+
441+
// TreatmentsByFlagSetsWithEvaluationOptions evaluate multiple feature flag names belonging to flag sets for a single user and a set of attributes at once
442+
func (c *SplitClient) TreatmentsByFlagSetsWithEvaluationOptions(key interface{}, flagSets []string, attributes map[string]interface{}, evaluationOptions dtos.EvaluationOptions) map[string]string {
443+
treatmentsResult := map[string]string{}
444+
flagSets = c.validateSets(flagSets)
445+
if flagSets == nil {
446+
return treatmentsResult
447+
}
448+
result := c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsByFlagSetsWithEvaluationOptions, telemetry.TreatmentsByFlagSetsWithEvaluationOptions, &evaluationOptions)
372449
for feature, treatmentResult := range result {
373450
treatmentsResult[feature] = treatmentResult.Treatment
374451
}
@@ -389,7 +466,12 @@ func (c *SplitClient) filterSetsAreInConfig(flagSets []string) []string {
389466

390467
// TreatmentsWithConfig evaluates multiple feature flag names for a single user and set of attributes at once and returns configurations
391468
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)
469+
return c.doTreatmentsCall(key, featureFlagNames, attributes, treatmentsWithConfig, telemetry.TreatmentsWithConfig, nil)
470+
}
471+
472+
// TreatmentsWithConfigAndEvaluationOptions evaluates multiple feature flag names for a single user and set of attributes at once and returns configurations
473+
func (c *SplitClient) TreatmentsWithConfigAndEvaluationOptions(key interface{}, featureFlagNames []string, attributes map[string]interface{}, evaluationOptions dtos.EvaluationOptions) map[string]TreatmentResult {
474+
return c.doTreatmentsCall(key, featureFlagNames, attributes, treatmentsWithConfigAndEvaluationOptions, telemetry.TreatmentsWithConfigByFlagSetAndEvaluationOptions, &evaluationOptions)
393475
}
394476

395477
// TreatmentsWithConfigByFlagSet evaluates multiple feature flag names belonging to a flag set for a single user and set of attributes at once and returns configurations
@@ -399,7 +481,17 @@ func (c *SplitClient) TreatmentsWithConfigByFlagSet(key interface{}, flagSet str
399481
if sets == nil {
400482
return treatmentsResult
401483
}
402-
return c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsWithConfigByFlagSet, telemetry.TreatmentsWithConfigByFlagSet)
484+
return c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsWithConfigByFlagSet, telemetry.TreatmentsWithConfigByFlagSet, nil)
485+
}
486+
487+
// TreatmentsWithConfigByFlagSetAndEvaluationOptions evaluates multiple feature flag names belonging to a flag set for a single user and set of attributes at once and returns configurations
488+
func (c *SplitClient) TreatmentsWithConfigByFlagSetAndEvaluationOptions(key interface{}, flagSet string, attributes map[string]interface{}, evaluationOptions dtos.EvaluationOptions) map[string]TreatmentResult {
489+
treatmentsResult := make(map[string]TreatmentResult)
490+
sets := c.validateSets([]string{flagSet})
491+
if sets == nil {
492+
return treatmentsResult
493+
}
494+
return c.doTreatmentsCallByFlagSets(key, sets, attributes, treatmentsWithConfigByFlagSetAndEvaluationOptions, telemetry.TreatmentsWithConfigByFlagSetAndEvaluationOptions, &evaluationOptions)
403495
}
404496

405497
// TreatmentsWithConfigByFlagSet evaluates multiple feature flag names belonging to a flag sets for a single user and set of attributes at once and returns configurations
@@ -409,7 +501,17 @@ func (c *SplitClient) TreatmentsWithConfigByFlagSets(key interface{}, flagSets [
409501
if flagSets == nil {
410502
return treatmentsResult
411503
}
412-
return c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsWithConfigByFlagSets, telemetry.TreatmentsWithConfigByFlagSets)
504+
return c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsWithConfigByFlagSets, telemetry.TreatmentsWithConfigByFlagSets, nil)
505+
}
506+
507+
// TreatmentsWithConfigByFlagSetsAndEvaluationOptions evaluates multiple feature flag names belonging to a flag sets for a single user and set of attributes at once and returns configurations
508+
func (c *SplitClient) TreatmentsWithConfigByFlagSetsAndEvaluationOptions(key interface{}, flagSets []string, attributes map[string]interface{}, evaluationOptions dtos.EvaluationOptions) map[string]TreatmentResult {
509+
treatmentsResult := make(map[string]TreatmentResult)
510+
flagSets = c.validateSets(flagSets)
511+
if flagSets == nil {
512+
return treatmentsResult
513+
}
514+
return c.doTreatmentsCallByFlagSets(key, flagSets, attributes, treatmentsWithConfigByFlagSetsAndEvaluationOptions, telemetry.TreatmentsWithConfigByFlagSetsAndEvaluationOptions, &evaluationOptions)
413515
}
414516

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

0 commit comments

Comments
 (0)