14
14
* limitations under the License. *
15
15
***************************************************************************/
16
16
17
- // Package decision provides CMAB client implementation
18
- package decision
17
+ // Package cmab provides CMAB client implementation
18
+ package cmab
19
19
20
20
import (
21
21
"bytes"
@@ -27,6 +27,9 @@ import (
27
27
"net/http"
28
28
"time"
29
29
30
+ "github.com/optimizely/go-sdk/v2/pkg/config"
31
+ "github.com/optimizely/go-sdk/v2/pkg/entities"
32
+ "github.com/optimizely/go-sdk/v2/pkg/event"
30
33
"github.com/optimizely/go-sdk/v2/pkg/logging"
31
34
)
32
35
@@ -88,16 +91,20 @@ type RetryConfig struct {
88
91
89
92
// DefaultCmabClient implements the CmabClient interface
90
93
type DefaultCmabClient struct {
91
- httpClient * http.Client
92
- retryConfig * RetryConfig
93
- logger logging.OptimizelyLogProducer
94
+ httpClient * http.Client
95
+ retryConfig * RetryConfig
96
+ logger logging.OptimizelyLogProducer
97
+ eventProcessor event.Processor
98
+ projectConfig config.ProjectConfig
94
99
}
95
100
96
101
// CmabClientOptions defines options for creating a CMAB client
97
102
type CmabClientOptions struct {
98
- HTTPClient * http.Client
99
- RetryConfig * RetryConfig
100
- Logger logging.OptimizelyLogProducer
103
+ HTTPClient * http.Client
104
+ RetryConfig * RetryConfig
105
+ Logger logging.OptimizelyLogProducer
106
+ EventProcessor event.Processor
107
+ ProjectConfig config.ProjectConfig
101
108
}
102
109
103
110
// NewDefaultCmabClient creates a new instance of DefaultCmabClient
@@ -119,9 +126,11 @@ func NewDefaultCmabClient(options CmabClientOptions) *DefaultCmabClient {
119
126
}
120
127
121
128
return & DefaultCmabClient {
122
- httpClient : httpClient ,
123
- retryConfig : retryConfig ,
124
- logger : logger ,
129
+ httpClient : httpClient ,
130
+ retryConfig : retryConfig ,
131
+ logger : logger ,
132
+ eventProcessor : options .EventProcessor ,
133
+ projectConfig : options .ProjectConfig ,
125
134
}
126
135
}
127
136
@@ -266,3 +275,97 @@ func (c *DefaultCmabClient) doFetch(ctx context.Context, url string, bodyBytes [
266
275
func (c * DefaultCmabClient ) validateResponse (response CMABResponse ) bool {
267
276
return len (response .Predictions ) > 0 && response .Predictions [0 ].VariationID != ""
268
277
}
278
+
279
+ // EventProcessor is an interface for processing events
280
+ type EventProcessor interface {
281
+ Process (userEvent interface {}) bool
282
+ }
283
+
284
+ // LogImpression logs a CMAB impression event
285
+ func (c * DefaultCmabClient ) LogImpression (
286
+ ctx context.Context ,
287
+ eventProcessor EventProcessor ,
288
+ projectConfig config.ProjectConfig ,
289
+ ruleID string ,
290
+ userID string ,
291
+ variationKey string ,
292
+ variationID string ,
293
+ attributes map [string ]interface {},
294
+ cmabUUID string ,
295
+ ) error {
296
+ // If no context is provided, create a background context
297
+ if ctx == nil {
298
+ ctx = context .Background ()
299
+ }
300
+
301
+ // Instead of directly creating an event, we'll delegate this to the client
302
+ // that has access to the event package
303
+ return fmt .Errorf ("CMAB impression logging not implemented" )
304
+ }
305
+
306
+ // TrackCMABDecision tracks a CMAB decision event
307
+ func (c * DefaultCmabClient ) TrackCMABDecision (
308
+ ruleID string ,
309
+ userID string ,
310
+ variationID string ,
311
+ variationKey string ,
312
+ attributes map [string ]interface {},
313
+ cmabUUID string ,
314
+ ) {
315
+ if c .eventProcessor == nil || c .projectConfig == nil {
316
+ c .logger .Debug ("Event processor or project config not available, not tracking impression" )
317
+ return
318
+ }
319
+
320
+ // Get experiment from project config
321
+ experiment , err := c .projectConfig .GetExperimentByID (ruleID )
322
+ if err != nil {
323
+ c .logger .Error ("Error getting experiment" , err )
324
+ return
325
+ }
326
+
327
+ // Create variation object
328
+ variation := entities.Variation {
329
+ ID : variationID ,
330
+ Key : variationKey ,
331
+ }
332
+
333
+ // Create user context
334
+ userContext := entities.UserContext {
335
+ ID : userID ,
336
+ Attributes : attributes ,
337
+ }
338
+
339
+ // Look for associated feature flag (if any)
340
+ flagKey := ""
341
+ featureList := c .projectConfig .GetFeatureList ()
342
+ for _ , feature := range featureList {
343
+ for _ , featureExperiment := range feature .FeatureExperiments {
344
+ if featureExperiment .ID == ruleID {
345
+ flagKey = feature .Key
346
+ break
347
+ }
348
+ }
349
+ if flagKey != "" {
350
+ break
351
+ }
352
+ }
353
+
354
+ // Create user event with CMAB impression
355
+ userEvent , shouldDispatch := event .CreateCMABImpressionUserEvent (
356
+ c .projectConfig ,
357
+ experiment ,
358
+ & variation ,
359
+ userContext ,
360
+ flagKey ,
361
+ experiment .Key , // ruleKey
362
+ "experiment" , // ruleType
363
+ true ,
364
+ cmabUUID ,
365
+ )
366
+
367
+ // Process the event if it should be dispatched
368
+ if shouldDispatch {
369
+ c .eventProcessor .ProcessEvent (userEvent )
370
+ }
371
+ }
0 commit comments