Skip to content

Commit 4d5f044

Browse files
(chore): add batch event sending (#56)
* add batch event sending * add a few unit tests for batching events. default batch size is 10 * only create visitor for batch. cleanup from review comments * added logging and took out prints * cleanup nits
1 parent 1d1942c commit 4d5f044

File tree

6 files changed

+225
-81
lines changed

6 files changed

+225
-81
lines changed

optimizely/event/dispatcher.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7+
"github.com/optimizely/go-sdk/optimizely/logging"
78
"net/http"
89
)
910

@@ -16,27 +17,25 @@ type Dispatcher interface {
1617
type HTTPEventDispatcher struct {
1718
}
1819

20+
var dispatcherLogger = logging.GetLogger("EventDispatcher")
21+
1922
// DispatchEvent dispatches event with callback
2023
func (*HTTPEventDispatcher) DispatchEvent(event LogEvent, callback func(success bool)) {
21-
// add to current batch or create new batch
22-
// does a batch have to contain a decision or can it just be impressions?
2324

2425
jsonValue, _ := json.Marshal(event.event)
25-
resp, err := http.Post(event.endPoint, "application/json", bytes.NewBuffer(jsonValue))
26-
fmt.Println(resp)
27-
fmt.Println(string(jsonValue))
26+
resp, err := http.Post( event.endPoint, "application/json", bytes.NewBuffer(jsonValue))
2827
// also check response codes
2928
// resp.StatusCode == 400 is an error
3029
success := true
3130

3231
if err != nil {
33-
fmt.Println(err)
32+
dispatcherLogger.Error("http.Post failed:", err)
3433
success = false
3534
} else {
3635
if resp.StatusCode == 204 {
3736
success = true
3837
} else {
39-
fmt.Printf("invalid response %d", resp.StatusCode)
38+
fmt.Printf("http.Post invalid response %d", resp.StatusCode)
4039
success = false
4140
}
4241
}

optimizely/event/events.go

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
package event
22

3-
// EventContext holds project-related contextual information about a UserEvent
4-
type EventContext struct {
5-
Revision string `json:"revision"`
6-
AccountID string `json:"account_id"`
7-
ClientVersion string `json:"client_version"`
8-
ProjectID string `json:"project_id"`
9-
ClientName string `json:"client_name"`
10-
AnonymizeIP bool `json:"anonymize_ip"`
11-
BotFiltering bool `json:"bot_filtering"`
3+
// Context holds project-related contextual information about a UserEvent
4+
type Context struct {
5+
Revision string `json:"revision"`
6+
AccountID string `json:"account_id"`
7+
ClientVersion string `json:"client_version"`
8+
ProjectID string `json:"project_id"`
9+
ClientName string `json:"client_name"`
10+
AnonymizeIP bool `json:"anonymize_ip"`
11+
BotFiltering bool `json:"bot_filtering"`
1212
attributeKeyToIDMap map[string]string `json:"attributeKeyToIdMap"`
1313
}
1414

15-
// UserEvent respresents a user event
15+
// UserEvent represents a user event
1616
type UserEvent struct {
17-
Timestamp int64 `json:"timestamp"`
17+
Timestamp int64 `json:"timestamp"`
1818
UUID string `json:"uuid"`
19-
EventContext EventContext
19+
EventContext Context
2020
VisitorID string
2121
Impression *ImpressionEvent
2222
Conversion *ConversionEvent
2323
}
2424

25-
// ImpressionEvent respresents an impression event
25+
// ImpressionEvent represents an impression event
2626
type ImpressionEvent struct {
2727
EntityID string `json:"entity_id"`
2828
Key string `json:"key"`
@@ -32,7 +32,7 @@ type ImpressionEvent struct {
3232
ExperimentID string `json:"experiment_id"`
3333
}
3434

35-
// ConversionEvent respresents a conversion event
35+
// ConversionEvent represents a conversion event
3636
type ConversionEvent struct {
3737
EntityID string `json:"entity_id"`
3838
Key string `json:"key"`
@@ -44,53 +44,53 @@ type ConversionEvent struct {
4444
Value *float64 `json:"value,omitempty"`
4545
}
4646

47-
// LogEvent respresents a log event
47+
// LogEvent represents a log event
4848
type LogEvent struct {
4949
endPoint string
50-
event EventBatch
50+
event Batch
5151
}
5252

53-
// EventBatch respresents Context about the event
54-
type EventBatch struct {
55-
Revision string `json:"revision"`
56-
AccountID string `json:"account_id"`
57-
ClientVersion string `json:"client_version"`
53+
// Context about the event to send in batch
54+
type Batch struct {
55+
Revision string `json:"revision"`
56+
AccountID string `json:"account_id"`
57+
ClientVersion string `json:"client_version"`
5858
Visitors []Visitor `json:"visitors"`
5959
ProjectID string `json:"project_id"`
6060
ClientName string `json:"client_name"`
6161
AnonymizeIP bool `json:"anonymize_ip"`
6262
EnrichDecisions bool `json:"enrich_decisions"`
6363
}
6464

65-
// Visitor respresents a visitor of an eventbatch
65+
// Visitor represents a visitor of an eventbatch
6666
type Visitor struct {
6767
Attributes []VisitorAttribute `json:"attributes"`
6868
Snapshots []Snapshot `json:"snapshots"`
6969
VisitorID string `json:"visitor_id"`
7070
}
7171

72-
// VisitorAttribute respresents an attribute of a visitor
72+
// VisitorAttribute represents an attribute of a visitor
7373
type VisitorAttribute struct {
7474
Value interface{} `json:"value"`
7575
Key string `json:"key"`
7676
AttributeType string `json:"type"`
7777
EntityID string `json:"entity_id"`
7878
}
7979

80-
// Snapshot respresents a snapshot of a visitor
80+
// Snapshot represents a snapshot of a visitor
8181
type Snapshot struct {
8282
Decisions []Decision `json:"decisions"`
8383
Events []SnapshotEvent `json:"events"`
8484
}
8585

86-
// Decision respresents a decision of a snapshot
86+
// Decision represents a decision of a snapshot
8787
type Decision struct {
8888
VariationID string `json:"variation_id"`
8989
CampaignID string `json:"campaign_id"`
9090
ExperimentID string `json:"experiment_id"`
9191
}
9292

93-
// SnapshotEvent respresents an event of a snapshot
93+
// SnapshotEvent represents an event of a snapshot
9494
type SnapshotEvent struct {
9595
EntityID string `json:"entity_id"`
9696
Key string `json:"key"`

optimizely/event/factory.go

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const eventEndPoint = "https://logx.optimizely.com/v1/events"
2020
const revenueKey = "revenue"
2121
const valueKey = "value"
2222

23-
func createLogEvent(event EventBatch) LogEvent {
23+
func createLogEvent(event Batch) LogEvent {
2424
return LogEvent{endPoint: eventEndPoint, event: event}
2525
}
2626

@@ -29,8 +29,8 @@ func makeTimestamp() int64 {
2929
}
3030

3131
// CreateEventContext creates and returns EventContext
32-
func CreateEventContext(projectID string, revision string, accountID string, anonymizeIP bool, botFiltering bool, attributeKeyToIDMap map[string]string) EventContext {
33-
context := EventContext{}
32+
func CreateEventContext(projectID string, revision string, accountID string, anonymizeIP bool, botFiltering bool, attributeKeyToIDMap map[string]string) Context {
33+
context := Context{}
3434
context.ProjectID = projectID
3535
context.Revision = revision
3636
context.AccountID = accountID
@@ -43,7 +43,7 @@ func CreateEventContext(projectID string, revision string, accountID string, ano
4343
return context
4444
}
4545

46-
func createImpressionEvent(context EventContext, experiment entities.Experiment,
46+
func createImpressionEvent(context Context, experiment entities.Experiment,
4747
variation entities.Variation, attributes map[string]interface{}) ImpressionEvent {
4848

4949
impression := ImpressionEvent{}
@@ -58,7 +58,7 @@ func createImpressionEvent(context EventContext, experiment entities.Experiment,
5858
}
5959

6060
// CreateImpressionUserEvent creates and returns ImpressionEvent for user
61-
func CreateImpressionUserEvent(context EventContext, experiment entities.Experiment,
61+
func CreateImpressionUserEvent(context Context, experiment entities.Experiment,
6262
variation entities.Variation,
6363
userContext entities.UserContext) UserEvent {
6464

@@ -74,8 +74,8 @@ func CreateImpressionUserEvent(context EventContext, experiment entities.Experim
7474
return userEvent
7575
}
7676

77-
func createImpressionBatchEvent(userEvent UserEvent) EventBatch {
78-
77+
// create an impression visitor
78+
func createImpressionVisitor(userEvent UserEvent) Visitor {
7979
decision := Decision{}
8080
decision.CampaignID = userEvent.Impression.CampaignID
8181
decision.ExperimentID = userEvent.Impression.ExperimentID
@@ -88,10 +88,12 @@ func createImpressionBatchEvent(userEvent UserEvent) EventBatch {
8888
dispatchEvent.UUID = guuid.New().String()
8989
dispatchEvent.Tags = make(map[string]interface{})
9090

91-
return createBatchEvent(userEvent, userEvent.Impression.Attributes, []Decision{decision}, []SnapshotEvent{dispatchEvent})
91+
visitor := createVisitor(userEvent, userEvent.Impression.Attributes, []Decision{decision}, []SnapshotEvent{dispatchEvent})
9292

93+
return visitor
9394
}
9495

96+
// create a conversion event
9597
func createConversionEvent(attributeKeyToIDMap map[string]string, event entities.Event, attributes map[string]interface{}, eventTags map[string]interface{}, botFiltering bool) ConversionEvent {
9698
conversion := ConversionEvent{}
9799

@@ -104,7 +106,7 @@ func createConversionEvent(attributeKeyToIDMap map[string]string, event entities
104106
}
105107

106108
// CreateConversionUserEvent creates and returns ConversionEvent for user
107-
func CreateConversionUserEvent(context EventContext, event entities.Event, userContext entities.UserContext, attributeKeyToIDMap map[string]string, eventTags map[string]interface{}) UserEvent {
109+
func CreateConversionUserEvent(context Context, event entities.Event, userContext entities.UserContext, attributeKeyToIDMap map[string]string, eventTags map[string]interface{}) UserEvent {
108110

109111
userEvent := UserEvent{}
110112
userEvent.Timestamp = makeTimestamp()
@@ -127,7 +129,21 @@ func CreateConversionUserEvent(context EventContext, event entities.Event, userC
127129
return userEvent
128130

129131
}
130-
func createConversionBatchEvent(userEvent UserEvent) EventBatch {
132+
133+
// create visitor from user event
134+
func createVisitorFromUserEvent(event UserEvent) Visitor {
135+
if event.Impression != nil {
136+
return createImpressionVisitor(event)
137+
}
138+
if event.Conversion != nil {
139+
return createConversionVisitor(event)
140+
}
141+
142+
return Visitor{}
143+
}
144+
145+
// create a conversion visitor
146+
func createConversionVisitor(userEvent UserEvent) Visitor {
131147

132148
dispatchEvent := SnapshotEvent{}
133149
dispatchEvent.Timestamp = makeTimestamp()
@@ -142,13 +158,15 @@ func createConversionBatchEvent(userEvent UserEvent) EventBatch {
142158
dispatchEvent.Value = userEvent.Conversion.Value
143159
}
144160

145-
return createBatchEvent(userEvent, userEvent.Conversion.Attributes, []Decision{}, []SnapshotEvent{dispatchEvent})
161+
visitor := createVisitor(userEvent, userEvent.Conversion.Attributes, []Decision{}, []SnapshotEvent{dispatchEvent})
162+
163+
return visitor
146164
}
147165

148-
func createBatchEvent(userEvent UserEvent, attributes []VisitorAttribute,
166+
// create a visitor
167+
func createVisitor(userEvent UserEvent, attributes []VisitorAttribute,
149168
decisions []Decision,
150-
dispatchEvents []SnapshotEvent) EventBatch {
151-
169+
dispatchEvents []SnapshotEvent) Visitor {
152170
snapShot := Snapshot{}
153171
snapShot.Decisions = decisions
154172
snapShot.Events = dispatchEvents
@@ -161,7 +179,14 @@ func createBatchEvent(userEvent UserEvent, attributes []VisitorAttribute,
161179
visitor.Snapshots = []Snapshot{snapShot}
162180
visitor.VisitorID = userEvent.VisitorID
163181

164-
eventBatch := EventBatch{}
182+
return visitor
183+
}
184+
185+
// create a batch event with visitor
186+
func createBatchEvent(userEvent UserEvent, visitor Visitor) Batch {
187+
188+
189+
eventBatch := Batch{}
165190
eventBatch.ProjectID = userEvent.EventContext.ProjectID
166191
eventBatch.Revision = userEvent.EventContext.Revision
167192
eventBatch.AccountID = userEvent.EventContext.AccountID
@@ -174,6 +199,7 @@ func createBatchEvent(userEvent UserEvent, attributes []VisitorAttribute,
174199
return eventBatch
175200
}
176201

202+
// get visitor attributes from user attributes
177203
func getEventAttributes(attributeKeyToIDMap map[string]string, attributes map[string]interface{}, botFiltering bool) []VisitorAttribute {
178204
var eventAttributes = []VisitorAttribute{}
179205

@@ -206,6 +232,7 @@ func getEventAttributes(attributeKeyToIDMap map[string]string, attributes map[st
206232
return eventAttributes
207233
}
208234

235+
// get revenue attribute
209236
func getRevenueValue(eventTags map[string]interface{}) (int64, error) {
210237
if value, ok := eventTags[revenueKey]; ok {
211238
return utils.GetIntValue(value)
@@ -214,6 +241,7 @@ func getRevenueValue(eventTags map[string]interface{}) (int64, error) {
214241
return 0, errors.New("No event tag found for revenue")
215242
}
216243

244+
// get a value attribute
217245
func getTagValue(eventTags map[string]interface{}) (float64, error) {
218246
if value, ok := eventTags[valueKey]; ok {
219247
return utils.GetFloatValue(value)

optimizely/event/factory_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func TestCreateConversionEventRevenue(t *testing.T) {
129129
assert.Equal(t, int64(55), *conversionUserEvent.Conversion.Revenue)
130130
assert.Equal(t, 25.1, *conversionUserEvent.Conversion.Value)
131131

132-
batch := createConversionBatchEvent(conversionUserEvent)
132+
batch := createBatchEvent(conversionUserEvent, createVisitorFromUserEvent(conversionUserEvent))
133133
assert.Equal(t, int64(55), *batch.Visitors[0].Snapshots[0].Events[0].Revenue)
134134
assert.Equal(t, 25.1, *batch.Visitors[0].Snapshots[0].Events[0].Value)
135135

0 commit comments

Comments
 (0)