Skip to content

[DRAFT][FSSDK-11178] Update impression event for CMAB #404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 109 additions & 11 deletions pkg/decision/cmab_client.go → pkg/cmab/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License. *
***************************************************************************/

// Package decision provides CMAB client implementation
package decision
// Package cmab provides CMAB client implementation
package cmab

import (
"bytes"
Expand All @@ -27,6 +27,9 @@ import (
"net/http"
"time"

"github.com/optimizely/go-sdk/v2/pkg/config"
"github.com/optimizely/go-sdk/v2/pkg/entities"
"github.com/optimizely/go-sdk/v2/pkg/event"
"github.com/optimizely/go-sdk/v2/pkg/logging"
)

Expand Down Expand Up @@ -88,16 +91,20 @@ type RetryConfig struct {

// DefaultCmabClient implements the CmabClient interface
type DefaultCmabClient struct {
httpClient *http.Client
retryConfig *RetryConfig
logger logging.OptimizelyLogProducer
httpClient *http.Client
retryConfig *RetryConfig
logger logging.OptimizelyLogProducer
eventProcessor event.Processor
projectConfig config.ProjectConfig
}

// CmabClientOptions defines options for creating a CMAB client
type CmabClientOptions struct {
HTTPClient *http.Client
RetryConfig *RetryConfig
Logger logging.OptimizelyLogProducer
HTTPClient *http.Client
RetryConfig *RetryConfig
Logger logging.OptimizelyLogProducer
EventProcessor event.Processor
ProjectConfig config.ProjectConfig
}

// NewDefaultCmabClient creates a new instance of DefaultCmabClient
Expand All @@ -119,9 +126,11 @@ func NewDefaultCmabClient(options CmabClientOptions) *DefaultCmabClient {
}

return &DefaultCmabClient{
httpClient: httpClient,
retryConfig: retryConfig,
logger: logger,
httpClient: httpClient,
retryConfig: retryConfig,
logger: logger,
eventProcessor: options.EventProcessor,
projectConfig: options.ProjectConfig,
}
}

Expand Down Expand Up @@ -266,3 +275,92 @@ func (c *DefaultCmabClient) doFetch(ctx context.Context, url string, bodyBytes [
func (c *DefaultCmabClient) validateResponse(response CMABResponse) bool {
return len(response.Predictions) > 0 && response.Predictions[0].VariationID != ""
}

// EventProcessor is an interface for processing events
type EventProcessor interface {
Process(userEvent interface{}) bool
}

// LogImpression logs a CMAB impression event
func (c *DefaultCmabClient) LogImpression(
ctx context.Context,
eventProcessor EventProcessor,
projectConfig config.ProjectConfig,
ruleID string,
userID string,
variationKey string,
variationID string,
attributes map[string]interface{},
cmabUUID string,
) error {
// Instead of directly creating an event, we'll delegate this to the client
// that has access to the event package
return fmt.Errorf("CMAB impression logging not implemented")
}

// TrackCMABDecision tracks a CMAB decision event
func (c *DefaultCmabClient) TrackCMABDecision(
ruleID string,
userID string,
variationID string,
variationKey string,
attributes map[string]interface{},
cmabUUID string,
) {
if c.eventProcessor == nil || c.projectConfig == nil {
c.logger.Debug("Event processor or project config not available, not tracking impression")
return
}

// Get experiment from project config
experiment, err := c.projectConfig.GetExperimentByID(ruleID)
if err != nil {
c.logger.Error("Error getting experiment", err)
return
}

// Create variation object
variation := entities.Variation{
ID: variationID,
Key: variationKey,
}

// Create user context
userContext := entities.UserContext{
ID: userID,
Attributes: attributes,
}

// Look for associated feature flag (if any)
flagKey := ""
featureList := c.projectConfig.GetFeatureList()
for _, feature := range featureList {
for _, featureExperiment := range feature.FeatureExperiments {
if featureExperiment.ID == ruleID {
flagKey = feature.Key
break
}
}
if flagKey != "" {
break
}
}

// Create user event with CMAB impression
userEvent, shouldDispatch := event.CreateCMABImpressionUserEvent(
c.projectConfig,
experiment,
&variation,
userContext,
flagKey,
experiment.Key, // ruleKey
"experiment", // ruleType
true,
cmabUUID,
)

// Process the event if it should be dispatched
if shouldDispatch {
c.eventProcessor.ProcessEvent(userEvent)
}
}
4 changes: 2 additions & 2 deletions pkg/decision/cmab_client_test.go → pkg/cmab/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License. *
***************************************************************************/

// Package decision //
package decision
// Package cmab //
package cmab

import (
"context"
Expand Down
19 changes: 17 additions & 2 deletions pkg/decision/cmab_service.go → pkg/cmab/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License. *
***************************************************************************/

// Package decision provides CMAB decision service implementation
package decision
// Package cmab provides CMAB decision service implementation
package cmab

import (
"encoding/json"
Expand Down Expand Up @@ -186,6 +186,21 @@ func (s *DefaultCmabService) fetchDecisionWithRetry(
variationID, err := s.cmabClient.FetchDecision(ruleID, userID, attributes, cmabUUID)
if err == nil {
reasons = append(reasons, fmt.Sprintf("Successfully fetched CMAB decision on attempt %d/%d", attempt+1, maxRetries))

// Get variation key from variation ID
// This might require additional logic depending on your implementation
variationKey := variationID // Simplified - you may need to look up the key

// Track the decision event
s.cmabClient.TrackCMABDecision(
ruleID,
userID,
variationID,
variationKey,
attributes,
cmabUUID,
)

return CmabDecision{
VariationID: variationID,
CmabUUID: cmabUUID,
Expand Down
Loading
Loading