Skip to content

Commit 8767636

Browse files
rashidspMichael Ng
authored and
Michael Ng
committed
feat(eventfactory): Event Factory and UserEventFactory (#188)
1 parent 5a0f58a commit 8767636

15 files changed

+1111
-69
lines changed

.rubocop_todo.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ Metrics/ParameterLists:
2424
- 'lib/optimizely/config_manager/http_project_config_manager.rb'
2525
- 'lib/optimizely.rb'
2626
- 'lib/optimizely/optimizely_factory.rb'
27+
- 'lib/optimizely/event/entity/impression_event.rb'
2728
- 'lib/optimizely/event/entity/snapshot_event.rb'
29+
- 'lib/optimizely/event/entity/conversion_event.rb'
30+
- 'lib/optimizely/event/entity/event_context.rb'
2831

2932
Naming/AccessorMethodName:
3033
Exclude:

lib/optimizely/event/entity/conversion_event.rb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,23 @@
1616
# limitations under the License.
1717
#
1818
require_relative 'user_event'
19+
require 'optimizely/helpers/date_time_utils'
1920
module Optimizely
2021
class ConversionEvent < UserEvent
21-
attr_reader :event_context, :event, :user_id, :visitor_attributes, :tags, :bot_filtering
22+
# Represents conversion event
23+
attr_reader :event, :user_id, :visitor_attributes, :tags, :bot_filtering
2224

2325
def initialize(
24-
event_context,
25-
event,
26-
user_id,
27-
visitor_attributes,
28-
tags,
29-
bot_filtering = nil
26+
event_context:,
27+
event:,
28+
user_id:,
29+
visitor_attributes:,
30+
tags:,
31+
bot_filtering:
3032
)
3133
@event_context = event_context
34+
@uuid = SecureRandom.uuid
35+
@timestamp = Helpers::DateTimeUtils.create_timestamp
3236
@event = event
3337
@user_id = user_id
3438
@visitor_attributes = visitor_attributes

lib/optimizely/event/entity/decision.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ module Optimizely
1919
class Decision
2020
attr_reader :campaign_id, :experiment_id, :variation_id
2121

22-
def initialize(campaign_id, experiment_id, variation_id)
22+
def initialize(campaign_id:, experiment_id:, variation_id:)
2323
@campaign_id = campaign_id
2424
@experiment_id = experiment_id
2525
@variation_id = variation_id

lib/optimizely/event/entity/event_context.rb

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,34 @@
1717
#
1818
module Optimizely
1919
class EventContext
20-
attr_reader :account_id, :project_id, :revision, :client_name,
21-
:client_version, :anonymize_ip
20+
attr_reader :account_id, :project_id, :anonymize_ip, :revision, :client_name,
21+
:client_version
22+
2223
def initialize(
23-
account_id,
24-
project_id,
25-
revision,
26-
client_name,
27-
client_version,
28-
anonymize_ip
24+
account_id:,
25+
project_id:,
26+
anonymize_ip:,
27+
revision:,
28+
client_name:,
29+
client_version:
2930
)
3031
@account_id = account_id
3132
@project_id = project_id
33+
@anonymize_ip = anonymize_ip
3234
@revision = revision
3335
@client_name = client_name
3436
@client_version = client_version
35-
@anonymize_ip = anonymize_ip
37+
end
38+
39+
def as_json
40+
{
41+
account_id: @account_id,
42+
project_id: @project_id,
43+
anonymize_ip: @anonymize_ip,
44+
revision: @revision,
45+
client_name: @client_name,
46+
client_version: @client_version
47+
}
3648
end
3749
end
3850
end

lib/optimizely/event/entity/impression_event.rb

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,28 @@
1616
# limitations under the License.
1717
#
1818
require_relative 'user_event'
19+
require 'optimizely/helpers/date_time_utils'
1920
module Optimizely
2021
class ImpressionEvent < UserEvent
21-
attr_reader :event_context, :user_id, :experiment, :variation, :visitor_attributes,
22-
:bot_filtering
22+
attr_reader :user_id, :experiment_layer_id, :experiment_id, :variation_id,
23+
:visitor_attributes, :bot_filtering
2324

2425
def initialize(
25-
event_context,
26-
user_id,
27-
experiment,
28-
variation,
29-
visitor_attributes,
30-
bot_filtering
26+
event_context:,
27+
user_id:,
28+
experiment_layer_id:,
29+
experiment_id:,
30+
variation_id:,
31+
visitor_attributes:,
32+
bot_filtering:
3133
)
3234
@event_context = event_context
35+
@uuid = SecureRandom.uuid
36+
@timestamp = Helpers::DateTimeUtils.create_timestamp
3337
@user_id = user_id
34-
@experiment = experiment
35-
@variation = variation
38+
@experiment_layer_id = experiment_layer_id
39+
@experiment_id = experiment_id
40+
@variation_id = variation_id
3641
@visitor_attributes = visitor_attributes
3742
@bot_filtering = bot_filtering
3843
end

lib/optimizely/event/entity/snapshot.rb

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,16 @@
1717
#
1818
module Optimizely
1919
class Snapshot
20-
attr_reader :decisions, :events
20+
attr_reader :events, :decisions
2121

22-
def initialize(events, decisions = nil)
22+
def initialize(events:, decisions: nil)
2323
@decisions = decisions
2424
@events = events
2525
end
2626

2727
def as_json
28-
hash = {
29-
events: @events,
30-
decisions: @decisions
31-
}
32-
hash.delete_if { |_key, value| value.nil? }
28+
hash = {events: @events}
29+
hash[:decisions] = @decisions unless @decisions.nil?
3330
hash
3431
end
3532
end

lib/optimizely/event/entity/snapshot_event.rb

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ class SnapshotEvent
2020
attr_reader :entity_id, :uuid, :key, :timestamp, :revenue, :value, :tags
2121

2222
def initialize(
23-
entity_id: nil,
24-
uuid: nil,
25-
key: nil,
26-
timestamp: nil,
23+
entity_id:,
24+
uuid:,
25+
key:,
26+
timestamp:,
2727
revenue: nil,
2828
value: nil,
2929
tags: nil
@@ -38,16 +38,10 @@ def initialize(
3838
end
3939

4040
def as_json
41-
hash = {
42-
entity_id: @entity_id,
43-
uuid: @uuid,
44-
key: @key,
45-
timestamp: @timestamp,
46-
revenue: @revenue,
47-
value: @value,
48-
tags: @tags
49-
}
50-
hash.delete_if { |_key, value| value.nil? }
41+
hash = {entity_id: @entity_id, uuid: @uuid, key: @key, timestamp: @timestamp}
42+
hash[:revenue] = @revenue unless @revenue.nil?
43+
hash[:value] = @value unless @value.nil?
44+
hash[:tags] = @tags unless @tags.nil?
5145
hash
5246
end
5347
end

lib/optimizely/event/entity/visitor.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@
1717
#
1818
module Optimizely
1919
class Visitor
20-
attr_reader :snapshots, :attributes, :visitor_id
21-
def initialize(snapshots, attributes, visitor_id)
20+
attr_reader :snapshots, :visitor_id, :attributes
21+
def initialize(snapshots:, visitor_id:, attributes:)
2222
@snapshots = snapshots
23-
@attributes = attributes
2423
@visitor_id = visitor_id
24+
@attributes = attributes
2525
end
2626

2727
def as_json
2828
{
2929
snapshots: @snapshots,
30-
attributes: @attributes,
31-
visitor_id: @visitor_id
30+
visitor_id: @visitor_id,
31+
attributes: @attributes
3232
}
3333
end
3434
end

lib/optimizely/event/entity/visitor_attribute.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
module Optimizely
1919
class VisitorAttribute
2020
attr_reader :entity_id, :key, :type, :value
21-
def initialize(entity_id, key, type, value)
21+
def initialize(entity_id:, key:, type:, value:)
2222
@entity_id = entity_id
2323
@key = key
2424
@type = type

lib/optimizely/event/event_factory.rb

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# frozen_string_literal: true
2+
3+
#
4+
# Copyright 2019, Optimizely and contributors
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
require_relative 'entity/event_batch'
19+
require_relative 'entity/conversion_event'
20+
require_relative 'entity/decision'
21+
require_relative 'entity/impression_event'
22+
require_relative 'entity/snapshot'
23+
require_relative 'entity/snapshot_event'
24+
require_relative 'entity/visitor'
25+
require 'optimizely/helpers/validator'
26+
module Optimizely
27+
class EventFactory
28+
# EventFactory builds LogEvent objects from a given user_event.
29+
class << self
30+
CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom'
31+
ENDPOINT = 'https://logx.optimizely.com/v1/events'
32+
POST_HEADERS = {'Content-Type' => 'application/json'}.freeze
33+
ACTIVATE_EVENT_KEY = 'campaign_activated'
34+
35+
def create_log_event(user_events, logger)
36+
@logger = logger
37+
builder = Optimizely::EventBatch::Builder.new
38+
39+
user_events = [user_events] unless user_events.is_a? Array
40+
41+
visitors = []
42+
user_context = nil
43+
user_events.each do |user_event|
44+
if user_event.is_a? Optimizely::ImpressionEvent
45+
visitor = create_impression_event_visitor(user_event)
46+
visitors.push(visitor)
47+
elsif user_event.is_a? Optimizely::ConversionEvent
48+
visitor = create_conversion_event_visitor(user_event)
49+
visitors.push(visitor)
50+
else
51+
@logger.log(Logger::WARN, 'invalid UserEvent added in a list.')
52+
next
53+
end
54+
user_context = user_event.event_context
55+
end
56+
57+
return nil if visitors.empty?
58+
59+
builder.with_account_id(user_context[:account_id])
60+
builder.with_project_id(user_context[:project_id])
61+
builder.with_client_version(user_context[:client_version])
62+
builder.with_revision(user_context[:revision])
63+
builder.with_client_name(user_context[:client_name])
64+
builder.with_anonymize_ip(user_context[:anonymize_ip])
65+
builder.with_enrich_decisions(true)
66+
67+
builder.with_visitors(visitors)
68+
event_batch = builder.build
69+
Event.new(:post, ENDPOINT, event_batch.as_json, POST_HEADERS)
70+
end
71+
72+
def build_attribute_list(user_attributes, project_config)
73+
visitor_attributes = []
74+
user_attributes&.keys&.each do |attribute_key|
75+
# Omit attribute values that are not supported by the log endpoint.
76+
attribute_value = user_attributes[attribute_key]
77+
next unless Helpers::Validator.attribute_valid?(attribute_key, attribute_value)
78+
79+
attribute_id = project_config.get_attribute_id attribute_key
80+
next if attribute_id.nil?
81+
82+
visitor_attributes.push(
83+
entity_id: attribute_id,
84+
key: attribute_key,
85+
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
86+
value: attribute_value
87+
)
88+
end
89+
90+
return unless Helpers::Validator.boolean? project_config.bot_filtering
91+
92+
# Append Bot Filtering Attribute
93+
visitor_attributes.push(
94+
entity_id: Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BOT_FILTERING'],
95+
key: Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BOT_FILTERING'],
96+
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
97+
value: project_config.bot_filtering
98+
)
99+
end
100+
101+
private
102+
103+
def create_impression_event_visitor(impression_event)
104+
decision = Optimizely::Decision.new(
105+
campaign_id: impression_event.experiment_layer_id,
106+
experiment_id: impression_event.experiment_id,
107+
variation_id: impression_event.variation_id
108+
)
109+
110+
snapshot_event = Optimizely::SnapshotEvent.new(
111+
entity_id: impression_event.experiment_layer_id,
112+
timestamp: impression_event.timestamp,
113+
uuid: impression_event.uuid,
114+
key: ACTIVATE_EVENT_KEY
115+
)
116+
117+
snapshot = Optimizely::Snapshot.new(
118+
events: [snapshot_event.as_json],
119+
decisions: [decision.as_json]
120+
)
121+
122+
visitor = Optimizely::Visitor.new(
123+
snapshots: [snapshot.as_json],
124+
visitor_id: impression_event.user_id,
125+
attributes: impression_event.visitor_attributes
126+
)
127+
visitor.as_json
128+
end
129+
130+
def create_conversion_event_visitor(conversion_event)
131+
revenue_value = Helpers::EventTagUtils.get_revenue_value(conversion_event.tags, @logger)
132+
numeric_value = Helpers::EventTagUtils.get_numeric_value(conversion_event.tags, @logger)
133+
snapshot_event = Optimizely::SnapshotEvent.new(
134+
entity_id: conversion_event.event['id'],
135+
timestamp: conversion_event.timestamp,
136+
uuid: conversion_event.uuid,
137+
key: conversion_event.event['key'],
138+
revenue: revenue_value,
139+
value: numeric_value,
140+
tags: conversion_event.tags
141+
)
142+
143+
snapshot = Optimizely::Snapshot.new(events: [snapshot_event.as_json])
144+
145+
visitor = Optimizely::Visitor.new(
146+
snapshots: [snapshot.as_json],
147+
visitor_id: conversion_event.user_id,
148+
attributes: conversion_event.visitor_attributes
149+
)
150+
visitor.as_json
151+
end
152+
end
153+
end
154+
end

0 commit comments

Comments
 (0)