@@ -6,28 +6,198 @@ module Optimizely
6
6
class Event
7
7
# Representation of an event which can be sent to the Optimizely logging endpoint.
8
8
9
- # Event API format
10
- OFFLINE_API_PATH = 'https://%{project_id}.log.optimizely.com/event'
9
+ attr_reader :http_verb
10
+ attr_reader :params
11
+ attr_reader :url
12
+ attr_reader :headers
13
+
14
+ def initialize ( http_verb , url , params , headers )
15
+ @http_verb = http_verb
16
+ @url = url
17
+ @params = params
18
+ @headers = headers
19
+ end
20
+
21
+ # Override equality operator to make two events with the same contents equal for testing purposes
22
+ def ==( event )
23
+ @http_verb == event . http_verb && @url == event . url && @params == event . params && @headers == event . headers
24
+ end
25
+ end
11
26
12
- # Gets/Sets event params.
27
+ class BaseEventBuilder
28
+ attr_reader :config
29
+ attr_reader :bucketer
13
30
attr_accessor :params
14
31
15
- def initialize ( params )
16
- @params = params
32
+ def initialize ( config , bucketer )
33
+ @config = config
34
+ @bucketer = bucketer
35
+ @params = { }
17
36
end
18
37
19
- def url
20
- # URL for sending impression/conversion event.
38
+ private
39
+
40
+ def add_common_params ( user_id , attributes )
41
+ # Add params which are used in both conversion and impression events.
21
42
#
22
- # project_id - ID for the project.
43
+ # user_id - ID for user.
44
+ # attributes - Hash representing user attributes and values which need to be recorded.
45
+
46
+ add_project_id
47
+ add_account_id
48
+ add_user_id ( user_id )
49
+ add_attributes ( attributes )
50
+ add_source
51
+ add_time
52
+ end
53
+ end
54
+
55
+ class EventBuilderV2 < BaseEventBuilder
56
+ CONVERSION_EVENT_ENDPOINT = 'https://p13nlog.dz.optimizely.com/log/event'
57
+ IMPRESSION_EVENT_ENDPOINT = 'https://p13nlog.dz.optimizely.com/log/decision'
58
+ POST_HEADERS = { 'Content-Type' => 'application/json' }
59
+
60
+ def create_impression_event ( experiment_key , variation_id , user_id , attributes )
61
+ # Create conversion Event to be sent to the logging endpoint.
62
+ #
63
+ # experiment_key - Experiment for which impression needs to be recorded.
64
+ # variation_id - ID for variation which would be presented to user.
65
+ # user_id - ID for user.
66
+ # attributes - Hash representing user attributes and values which need to be recorded.
67
+ #
68
+ # Returns event hash encapsulating the impression event.
69
+
70
+ @params = { }
71
+ add_common_params ( user_id , attributes )
72
+ add_decision ( experiment_key , variation_id )
73
+ add_attributes ( attributes )
74
+ Event . new ( :post , IMPRESSION_EVENT_ENDPOINT , @params , POST_HEADERS )
75
+ end
76
+
77
+ def create_conversion_event ( event_key , user_id , attributes , event_value , experiment_keys )
78
+ # Create conversion Event to be sent to the logging endpoint.
79
+ #
80
+ # event_key - Event key representing the event which needs to be recorded.
81
+ # user_id - ID for user.
82
+ # attributes - Hash representing user attributes and values which need to be recorded.
83
+ # event_value - Value associated with the event. Can be used to represent revenue in cents.
84
+ # experiment_keys - Array of valid experiment keys for the event
85
+ #
86
+ # Returns event hash encapsulating the conversion event.
87
+
88
+ @params = { }
89
+ add_common_params ( user_id , attributes )
90
+ add_conversion_event ( event_key , event_value )
91
+ add_layer_states ( user_id , experiment_keys )
92
+ Event . new ( :post , CONVERSION_EVENT_ENDPOINT , @params , POST_HEADERS )
93
+ end
94
+
95
+ private
96
+
97
+ def add_common_params ( user_id , attributes )
98
+ super
99
+ @params [ 'isGlobalHoldback' ] = false
100
+ end
101
+
102
+ def add_project_id
103
+ @params [ 'projectId' ] = @config . project_id
104
+ end
105
+
106
+ def add_account_id
107
+ @params [ 'accountId' ] = @config . account_id
108
+ end
109
+
110
+ def add_user_id ( user_id )
111
+ @params [ 'visitorId' ] = user_id
112
+ end
113
+
114
+ def add_attributes ( attributes )
115
+ @params [ 'userFeatures' ] = [ ]
116
+
117
+ return if attributes . nil?
118
+
119
+ attributes . keys . each do |attribute_key |
120
+ # Omit falsy attribute values
121
+ attribute_value = attributes [ attribute_key ]
122
+ next unless attribute_value
123
+
124
+ # Skip attributes not in the datafile
125
+ attribute_id = @config . get_attribute_id ( attribute_key )
126
+ next unless attribute_id
127
+
128
+ feature = {
129
+ 'id' => attribute_id ,
130
+ 'name' => attribute_key ,
131
+ 'type' => 'custom' ,
132
+ 'value' => attribute_value ,
133
+ 'shouldIndex' => true ,
134
+ }
135
+ @params [ 'userFeatures' ] . push ( feature )
136
+ end
137
+ end
138
+
139
+ def add_decision ( experiment_key , variation_id )
140
+ experiment_id = @config . get_experiment_id ( experiment_key )
141
+ @params [ 'layerId' ] = @config . experiment_key_map [ experiment_key ] [ 'layerId' ]
142
+ @params [ 'decision' ] = {
143
+ 'variationId' => variation_id ,
144
+ 'experimentId' => experiment_id ,
145
+ 'isLayerHoldback' => false ,
146
+ }
147
+ end
148
+
149
+ def add_conversion_event ( event_key , event_value )
150
+ # Add conversion event information to the event.
23
151
#
24
- # Returns URL for event API.
152
+ # event_key - Event key representing the event which needs to be recorded.
153
+ # event_value - Value associated with the event. Can be used to represent revenue in cents.
154
+
155
+ event_id = @config . event_key_map [ event_key ] [ 'id' ]
156
+ event_name = @config . event_key_map [ event_key ] [ 'key' ]
157
+
158
+ @params [ 'eventEntityId' ] = event_id
159
+ @params [ 'eventFeatures' ] = [ ]
160
+ @params [ 'eventName' ] = event_name
161
+ @params [ 'eventMetrics' ] = [ ]
25
162
26
- sprintf ( OFFLINE_API_PATH , project_id : @params [ Params ::PROJECT_ID ] )
163
+ if event_value
164
+ @params [ 'eventMetrics' ] . push ( {
165
+ 'name' => 'revenue' ,
166
+ 'value' => event_value ,
167
+ } )
168
+ end
169
+ end
170
+
171
+ def add_layer_states ( user_id , experiment_keys )
172
+ @params [ 'layerStates' ] = [ ]
173
+
174
+ experiment_keys . each do |experiment_key |
175
+ variation_id = @bucketer . bucket ( experiment_key , user_id )
176
+ experiment_id = @config . experiment_key_map [ experiment_key ] [ 'id' ]
177
+ layer_state = {
178
+ 'layerId' => @config . experiment_key_map [ experiment_key ] [ 'layerId' ] ,
179
+ 'decision' => {
180
+ 'variationId' => variation_id ,
181
+ 'experimentId' => experiment_id ,
182
+ 'isLayerHoldback' => false ,
183
+ } ,
184
+ 'actionTriggered' => true ,
185
+ }
186
+ @params [ 'layerStates' ] . push ( layer_state )
187
+ end
188
+ end
189
+
190
+ def add_source
191
+ @params [ 'clientEngine' ] = 'ruby-sdk'
192
+ @params [ 'clientVersion' ] = VERSION
193
+ end
194
+
195
+ def add_time
196
+ @params [ 'timestamp' ] = ( Time . now . to_f * 1000 ) . to_i
27
197
end
28
198
end
29
199
30
- class EventBuilder
200
+ class EventBuilderV1 < BaseEventBuilder
31
201
# Class which encapsulates methods to build events for tracking impressions and conversions.
32
202
33
203
# Attribute mapping format
@@ -36,15 +206,8 @@ class EventBuilder
36
206
# Experiment mapping format
37
207
EXPERIMENT_PARAM_FORMAT = '%{experiment_prefix}%{experiment_id}'
38
208
39
- attr_accessor :config
40
- attr_accessor :bucketer
41
- attr_accessor :params
42
-
43
- def initialize ( config , bucketer )
44
- @config = config
45
- @bucketer = bucketer
46
- @params = { }
47
- end
209
+ # Event endpoint path
210
+ OFFLINE_API_PATH = 'https://%{project_id}.log.optimizely.com/event'
48
211
49
212
def create_impression_event ( experiment_key , variation_id , user_id , attributes )
50
213
# Create conversion Event to be sent to the logging endpoint.
@@ -60,7 +223,7 @@ def create_impression_event(experiment_key, variation_id, user_id, attributes)
60
223
add_common_params ( user_id , attributes )
61
224
add_impression_goal ( experiment_key )
62
225
add_experiment ( experiment_key , variation_id )
63
- Event . new ( @params )
226
+ Event . new ( :get , sprintf ( OFFLINE_API_PATH , project_id : @params [ Params :: PROJECT_ID ] ) , @params , { } )
64
227
end
65
228
66
229
def create_conversion_event ( event_key , user_id , attributes , event_value , experiment_keys )
@@ -71,12 +234,14 @@ def create_conversion_event(event_key, user_id, attributes, event_value, experim
71
234
# attributes - Hash representing user attributes and values which need to be recorded.
72
235
# event_value - Value associated with the event. Can be used to represent revenue in cents.
73
236
# experiment_keys - Array of valid experiment keys for the goal
237
+ #
238
+ # Returns event hash encapsulating the conversion event.
74
239
75
240
@params = { }
76
241
add_common_params ( user_id , attributes )
77
242
add_conversion_goal ( event_key , event_value )
78
243
add_experiment_variation_params ( user_id , experiment_keys )
79
- Event . new ( @params )
244
+ Event . new ( :get , sprintf ( OFFLINE_API_PATH , project_id : @params [ Params :: PROJECT_ID ] ) , @params , { } )
80
245
end
81
246
82
247
private
@@ -128,20 +293,6 @@ def add_time
128
293
@params [ Params ::TIME ] = Time . now . strftime ( '%s' ) . to_i
129
294
end
130
295
131
- def add_common_params ( user_id , attributes )
132
- # Add params which are used same in both conversion and impression events.
133
- #
134
- # user_id - ID for user.
135
- # attributes - Hash representing user attributes and values which need to be recorded.
136
-
137
- add_project_id
138
- add_account_id
139
- add_user_id ( user_id )
140
- add_attributes ( attributes )
141
- add_source
142
- add_time
143
- end
144
-
145
296
def add_impression_goal ( experiment_key )
146
297
# Add impression goal information to the event.
147
298
#
0 commit comments