securenative 0.1.22 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +62 -64
- data/lib/config/configuration_builder.rb +4 -3
- data/lib/config/configuration_manager.rb +2 -1
- data/lib/config/securenative_options.rb +4 -3
- data/lib/utils/request_utils.rb +20 -5
- data/out/production/securenative-ruby/api_manager.rb +13 -5
- data/out/production/securenative-ruby/config/configuration_builder.rb +6 -9
- data/out/production/securenative-ruby/config/configuration_manager.rb +24 -23
- data/out/production/securenative-ruby/config/securenative_options.rb +8 -5
- data/out/production/securenative-ruby/context/hanami_context.rb +42 -0
- data/out/production/securenative-ruby/context/rails_context.rb +44 -0
- data/out/production/securenative-ruby/context/securenative_context.rb +35 -8
- data/out/production/securenative-ruby/context/sinatra_context.rb +42 -0
- data/out/production/securenative-ruby/event_manager.rb +15 -14
- data/out/production/securenative-ruby/http/{http_response.rb → secure_native_http_response.rb} +1 -1
- data/out/production/securenative-ruby/http/securenative_http_client.rb +23 -5
- data/out/production/securenative-ruby/models/event_options.rb +23 -1
- data/out/production/securenative-ruby/models/request_context.rb +2 -2
- data/out/production/securenative-ruby/models/sdk_event.rb +22 -6
- data/out/production/securenative-ruby/models/user_traits.rb +1 -1
- data/out/production/securenative-ruby/models/verify_result.rb +5 -1
- data/out/production/securenative-ruby/securenative.rb +2 -10
- data/out/production/securenative-ruby/utils/date_utils.rb +1 -1
- data/out/production/securenative-ruby/utils/encryption_utils.rb +38 -24
- data/out/production/securenative-ruby/utils/request_utils.rb +53 -7
- data/out/production/securenative-ruby/utils/secure_native_logger.rb +6 -6
- data/out/production/securenative-ruby/utils/version_utils.rb +5 -6
- data/out/test/securenative-ruby/spec_api_manager.rb +37 -31
- data/out/test/securenative-ruby/spec_context_builder.rb +52 -34
- data/out/test/securenative-ruby/spec_encryption_utils.rb +13 -13
- data/out/test/securenative-ruby/spec_event_manager.rb +49 -15
- data/out/test/securenative-ruby/spec_helper.rb +8 -0
- data/out/test/securenative-ruby/spec_request_utils.rb +25 -0
- data/out/test/securenative-ruby/spec_sdk_event.rb +24 -0
- data/out/test/securenative-ruby/spec_securenative.rb +35 -39
- data/out/test/securenative-ruby/spec_securenative_http_client.rb +13 -5
- data/out/test/securenative-ruby/spec_signature_utils.rb +1 -1
- data/out/test/securenative-ruby/spec_version_util.rb +10 -0
- data/securenative.gemspec +1 -1
- metadata +9 -4
- data/out/production/securenative-ruby/event_options.rb +0 -32
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'enums/failover_strategy'
|
4
|
+
|
3
5
|
class SecureNativeOptions
|
4
|
-
attr_reader :api_key, :api_url, :interval, :max_events, :timeout, :auto_send, :disable, :log_level, :fail_over_strategy
|
5
|
-
attr_writer :api_key, :api_url, :interval, :max_events, :timeout, :auto_send, :disable, :log_level, :fail_over_strategy
|
6
|
+
attr_reader :api_key, :api_url, :interval, :max_events, :timeout, :auto_send, :disable, :log_level, :fail_over_strategy, :proxy_headers
|
7
|
+
attr_writer :api_key, :api_url, :interval, :max_events, :timeout, :auto_send, :disable, :log_level, :fail_over_strategy, :proxy_headers
|
6
8
|
|
7
|
-
def initialize(api_key
|
8
|
-
max_events
|
9
|
-
fail_over_strategy
|
9
|
+
def initialize(api_key: nil, api_url: "https://api.securenative.com/collector/api/v1", interval: 1000,
|
10
|
+
max_events: 1000, timeout: 1500, auto_send: true, disable: false, log_level: "FATAL",
|
11
|
+
fail_over_strategy: FailOverStrategy::FAIL_OPEN, proxy_headers: [])
|
10
12
|
@api_key = api_key
|
11
13
|
@api_url = api_url
|
12
14
|
@interval = interval
|
@@ -16,5 +18,6 @@ class SecureNativeOptions
|
|
16
18
|
@disable = disable
|
17
19
|
@log_level = log_level
|
18
20
|
@fail_over_strategy = fail_over_strategy
|
21
|
+
@proxy_headers = proxy_headers
|
19
22
|
end
|
20
23
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class HanamiContext
|
4
|
+
SECURENATIVE_COOKIE = '_sn'
|
5
|
+
|
6
|
+
def self.get_client_token(request)
|
7
|
+
begin
|
8
|
+
request.env[SECURENATIVE_COOKIE]
|
9
|
+
rescue StandardError
|
10
|
+
begin
|
11
|
+
request.cookies[SECURENATIVE_COOKIE]
|
12
|
+
rescue StandardError
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get_url(request)
|
19
|
+
begin
|
20
|
+
request.env['REQUEST_PATH']
|
21
|
+
rescue StandardError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.get_method(request)
|
27
|
+
begin
|
28
|
+
request.request_method
|
29
|
+
rescue StandardError
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.get_headers(request)
|
35
|
+
begin
|
36
|
+
# Note: At the moment we're filtering out everything but user-agent since ruby's payload is way too big
|
37
|
+
{ 'user-agent' => request.env['HTTP_USER_AGENT'] }
|
38
|
+
rescue StandardError
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RailsContext
|
4
|
+
SECURENATIVE_COOKIE = '_sn'
|
5
|
+
|
6
|
+
def self.get_client_token(request)
|
7
|
+
begin
|
8
|
+
request.cookies[SECURENATIVE_COOKIE]
|
9
|
+
rescue StandardError
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_url(request)
|
15
|
+
begin
|
16
|
+
# Rails >= 3.x
|
17
|
+
request.fullpath
|
18
|
+
rescue StandardError
|
19
|
+
begin
|
20
|
+
# Rails < 3.x & Sinatra
|
21
|
+
request.url if url.nil?
|
22
|
+
rescue StandardError
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.get_method(request)
|
29
|
+
begin
|
30
|
+
request.method
|
31
|
+
rescue StandardError
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.get_headers(request)
|
37
|
+
begin
|
38
|
+
# Note: At the moment we're filtering out everything but user-agent since ruby's payload is way too big
|
39
|
+
{ 'user-agent' => request.env['HTTP_USER_AGENT'] }
|
40
|
+
rescue StandardError
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,10 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'utils/request_utils'
|
4
|
+
require 'utils/utils'
|
5
|
+
require 'context/rails_context'
|
6
|
+
require 'context/hanami_context'
|
7
|
+
require 'context/sinatra_context'
|
8
|
+
|
3
9
|
class SecureNativeContext
|
4
10
|
attr_reader :client_token, :ip, :remote_ip, :headers, :url, :http_method, :body
|
5
11
|
attr_writer :client_token, :ip, :remote_ip, :headers, :url, :http_method, :body
|
6
12
|
|
7
|
-
|
13
|
+
SECURENATIVE_COOKIE = '_sn'
|
14
|
+
|
15
|
+
def initialize(client_token: '', ip: '', remote_ip: '', headers: nil, url: '', http_method: '', body: '')
|
8
16
|
@client_token = client_token
|
9
17
|
@ip = ip
|
10
18
|
@remote_ip = remote_ip
|
@@ -19,22 +27,41 @@ class SecureNativeContext
|
|
19
27
|
end
|
20
28
|
|
21
29
|
def self.from_http_request(request)
|
30
|
+
client_token = RailsContext.get_client_token(request)
|
31
|
+
client_token = SinatraContext.get_client_token(request) if client_token.nil?
|
32
|
+
client_token = HanamiContext.get_client_token(request) if client_token.nil?
|
33
|
+
|
22
34
|
begin
|
23
|
-
|
35
|
+
headers = RailsContext.get_headers(request)
|
36
|
+
headers = SinatraContext.get_headers(request) if headers.nil?
|
37
|
+
headers = HanamiContext.get_headers(request) if headers.nil?
|
38
|
+
|
39
|
+
# Standard Ruby request
|
40
|
+
headers = request.header.to_hash if headers.nil?
|
24
41
|
rescue StandardError
|
25
|
-
|
42
|
+
headers = []
|
26
43
|
end
|
27
44
|
|
45
|
+
url = RailsContext.get_url(request)
|
46
|
+
url = SinatraContext.get_url(request) if url.nil?
|
47
|
+
url = HanamiContext.get_url(request) if url.nil?
|
48
|
+
url = '' if url.nil?
|
49
|
+
|
50
|
+
method = RailsContext.get_method(request)
|
51
|
+
method = SinatraContext.get_method(request) if method.nil?
|
52
|
+
method = HanamiContext.get_method(request) if method.nil?
|
53
|
+
method = '' if method.nil?
|
54
|
+
|
28
55
|
begin
|
29
|
-
|
56
|
+
body = request.body.to_s
|
30
57
|
rescue StandardError
|
31
|
-
|
58
|
+
body = ''
|
32
59
|
end
|
33
60
|
|
34
61
|
client_token = RequestUtils.get_secure_header_from_request(headers) if Utils.null_or_empty?(client_token)
|
35
62
|
|
36
|
-
SecureNativeContext.new(
|
37
|
-
|
38
|
-
|
63
|
+
SecureNativeContext.new(client_token: client_token, ip: RequestUtils.get_client_ip_from_request(request),
|
64
|
+
remote_ip: RequestUtils.get_remote_ip_from_request(request),
|
65
|
+
headers: headers, url: url, http_method: method || '', body: body)
|
39
66
|
end
|
40
67
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SinatraContext
|
4
|
+
SECURENATIVE_COOKIE = '_sn'
|
5
|
+
|
6
|
+
def self.get_client_token(request)
|
7
|
+
begin
|
8
|
+
request.env[SECURENATIVE_COOKIE]
|
9
|
+
rescue StandardError
|
10
|
+
begin
|
11
|
+
request.cookies[SECURENATIVE_COOKIE]
|
12
|
+
rescue StandardError
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get_url(request)
|
19
|
+
begin
|
20
|
+
request.env['REQUEST_URI']
|
21
|
+
rescue StandardError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.get_method(request)
|
27
|
+
begin
|
28
|
+
request.env['REQUEST_METHOD']
|
29
|
+
rescue StandardError
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.get_headers(request)
|
35
|
+
begin
|
36
|
+
# Note: At the moment we're filtering out everything but user-agent since ruby's payload is way too big
|
37
|
+
{ 'user-agent' => request.env['HTTP_USER_AGENT'] }
|
38
|
+
rescue StandardError
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -4,6 +4,7 @@ require 'utils/secure_native_logger'
|
|
4
4
|
require 'config/securenative_options'
|
5
5
|
require 'http/securenative_http_client'
|
6
6
|
require 'errors/securenative_sdk_error'
|
7
|
+
require 'errors/securenative_http_error'
|
7
8
|
|
8
9
|
class QueueItem
|
9
10
|
attr_reader :url, :body, :retry_sending
|
@@ -36,7 +37,7 @@ class EventManager
|
|
36
37
|
@attempt = 0
|
37
38
|
@coefficients = [1, 1, 2, 3, 5, 8, 13]
|
38
39
|
|
39
|
-
@thread = Thread.new {run}
|
40
|
+
@thread = Thread.new { run }
|
40
41
|
end
|
41
42
|
|
42
43
|
def send_async(event, resource_path)
|
@@ -45,7 +46,7 @@ class EventManager
|
|
45
46
|
return
|
46
47
|
end
|
47
48
|
|
48
|
-
item = QueueItem(resource_path,
|
49
|
+
item = QueueItem.new(resource_path, EventManager.serialize(event).to_json, false)
|
49
50
|
@queue.append(item)
|
50
51
|
end
|
51
52
|
|
@@ -62,11 +63,11 @@ class EventManager
|
|
62
63
|
end
|
63
64
|
|
64
65
|
SecureNativeLogger.debug("Attempting to send event #{event}")
|
65
|
-
res = @http_client.post(resource_path,
|
66
|
+
res = @http_client.post(resource_path, EventManager.serialize(event).to_json)
|
66
67
|
|
67
|
-
if res.
|
68
|
-
SecureNativeLogger.info(
|
69
|
-
item = QueueItem(resource_path,
|
68
|
+
if res.nil? || res.code != '200'
|
69
|
+
SecureNativeLogger.info("SecureNative failed to call endpoint #{resource_path} with event #{event}. adding back to queue")
|
70
|
+
item = QueueItem.new(resource_path, EventManager.serialize(event).to_json, retry_sending)
|
70
71
|
@queue.append(item)
|
71
72
|
end
|
72
73
|
|
@@ -81,20 +82,20 @@ class EventManager
|
|
81
82
|
@queue.each do |item|
|
82
83
|
begin
|
83
84
|
res = @http_client.post(item.url, item.body)
|
84
|
-
if res.
|
85
|
+
if res.code == '401'
|
85
86
|
item.retry_sending = false
|
86
|
-
elsif res.
|
87
|
+
elsif res.code != '200'
|
87
88
|
raise SecureNativeHttpError, res.status_code
|
88
89
|
end
|
89
|
-
SecureNativeLogger.debug(
|
90
|
+
SecureNativeLogger.debug("Event successfully sent; #{item.body}")
|
90
91
|
return res
|
91
92
|
rescue StandardError => e
|
92
|
-
SecureNativeLogger.error(
|
93
|
+
SecureNativeLogger.error("Failed to send event; #{e}")
|
93
94
|
if item.retry_sending
|
94
95
|
@attempt = 0 if @coefficients.length == @attempt + 1
|
95
96
|
|
96
97
|
back_off = @coefficients[@attempt] * @options.interval
|
97
|
-
SecureNativeLogger.debug(
|
98
|
+
SecureNativeLogger.debug("Automatic back-off of #{back_off}")
|
98
99
|
@send_enabled = false
|
99
100
|
sleep back_off
|
100
101
|
@send_enabled = true
|
@@ -120,10 +121,10 @@ class EventManager
|
|
120
121
|
SecureNativeLogger.debug('Attempting to stop automatic event persistence')
|
121
122
|
begin
|
122
123
|
flush
|
123
|
-
@thread&.stop
|
124
|
+
@thread&.stop?
|
124
125
|
SecureNativeLogger.debug('Stopped event persistence')
|
125
126
|
rescue StandardError => e
|
126
|
-
SecureNativeLogger.error(
|
127
|
+
SecureNativeLogger.error("Could not stop event scheduler; #{e}")
|
127
128
|
end
|
128
129
|
end
|
129
130
|
end
|
@@ -145,7 +146,7 @@ class EventManager
|
|
145
146
|
fp: obj.request.fp,
|
146
147
|
ip: obj.request.ip,
|
147
148
|
remoteIp: obj.request.remote_ip,
|
148
|
-
|
149
|
+
method: obj.request.http_method || '',
|
149
150
|
url: obj.request.url,
|
150
151
|
headers: obj.request.headers
|
151
152
|
},
|
@@ -1,18 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
require 'json'
|
6
|
+
require 'utils/version_utils'
|
7
|
+
require 'utils/secure_native_logger'
|
4
8
|
|
5
9
|
class SecureNativeHttpClient
|
6
10
|
AUTHORIZATION_HEADER = 'Authorization'
|
7
11
|
VERSION_HEADER = 'SN-Version'
|
8
12
|
USER_AGENT_HEADER = 'User-Agent'
|
9
|
-
USER_AGENT_HEADER_VALUE = 'SecureNative-
|
13
|
+
USER_AGENT_HEADER_VALUE = 'SecureNative-ruby'
|
10
14
|
CONTENT_TYPE_HEADER = 'Content-Type'
|
11
15
|
CONTENT_TYPE_HEADER_VALUE = 'application/json'
|
12
16
|
|
13
17
|
def initialize(securenative_options)
|
14
18
|
@options = securenative_options
|
15
|
-
@client = HTTPClient.new
|
16
19
|
end
|
17
20
|
|
18
21
|
def _headers
|
@@ -25,8 +28,23 @@ class SecureNativeHttpClient
|
|
25
28
|
end
|
26
29
|
|
27
30
|
def post(path, body)
|
28
|
-
|
31
|
+
uri = URI.parse("#{@options.api_url}/#{path}")
|
29
32
|
headers = _headers
|
30
|
-
|
33
|
+
|
34
|
+
client = Net::HTTP.new(uri.host, uri.port)
|
35
|
+
client.use_ssl = true
|
36
|
+
client.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
37
|
+
|
38
|
+
request = Net::HTTP::Post.new(uri.request_uri, headers)
|
39
|
+
request.body = body
|
40
|
+
|
41
|
+
res = nil
|
42
|
+
begin
|
43
|
+
res = client.request(request)
|
44
|
+
rescue StandardError => e
|
45
|
+
SecureNativeLogger.error("Failed to send request; #{e}")
|
46
|
+
return res
|
47
|
+
end
|
48
|
+
res
|
31
49
|
end
|
32
50
|
end
|
@@ -1,10 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'models/event_options'
|
4
|
+
require 'models/user_traits'
|
5
|
+
require 'errors/securenative_invalid_options_error'
|
6
|
+
|
3
7
|
class EventOptions
|
4
8
|
attr_reader :event, :user_id, :user_traits, :context, :properties, :timestamp
|
5
9
|
attr_writer :event, :user_id, :user_traits, :context, :properties, :timestamp
|
6
10
|
|
7
|
-
|
11
|
+
MAX_PROPERTIES_SIZE = 10
|
12
|
+
|
13
|
+
def initialize(event: nil, user_id: nil, user_traits: nil, user_name: nil, email: nil, phone: nil, created_at: nil, context: nil, properties: nil, timestamp: nil)
|
14
|
+
if !properties.nil? && properties.length > MAX_PROPERTIES_SIZE
|
15
|
+
raise SecureNativeInvalidOptionsError, "You can have only up to #{MAX_PROPERTIES_SIZE} custom properties"
|
16
|
+
end
|
17
|
+
|
18
|
+
if user_traits.nil?
|
19
|
+
if user_name && email && phone && created_at
|
20
|
+
user_traits = UserTraits(user_name, email, phone, created_at)
|
21
|
+
elsif user_name && email && phone
|
22
|
+
user_traits = UserTraits(user_name, email, phone)
|
23
|
+
elsif user_name && email
|
24
|
+
user_traits = UserTraits(user_name, email)
|
25
|
+
else
|
26
|
+
user_traits = UserTraits.new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
8
30
|
@event = event
|
9
31
|
@user_id = user_id
|
10
32
|
@user_traits = user_traits
|
@@ -4,7 +4,7 @@ class RequestContext
|
|
4
4
|
attr_reader :cid, :vid, :fp, :ip, :remote_ip, :headers, :url, :http_method
|
5
5
|
attr_writer :cid, :vid, :fp, :ip, :remote_ip, :headers, :url, :http_method
|
6
6
|
|
7
|
-
def initialize(cid
|
7
|
+
def initialize(cid: nil, vid: nil, fp: nil, ip: nil, remote_ip: nil, headers: nil, url: nil, http_method: nil)
|
8
8
|
@cid = cid
|
9
9
|
@vid = vid
|
10
10
|
@fp = fp
|
@@ -12,7 +12,7 @@ class RequestContext
|
|
12
12
|
@remote_ip = remote_ip
|
13
13
|
@headers = headers
|
14
14
|
@url = url
|
15
|
-
@method =
|
15
|
+
@method = http_method
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -1,14 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'context/securenative_context'
|
4
|
+
require 'errors/securenative_invalid_options_error'
|
5
|
+
require 'utils/encryption_utils'
|
6
|
+
require 'utils/date_utils'
|
7
|
+
require 'models/request_context'
|
8
|
+
require 'securerandom'
|
9
|
+
|
3
10
|
class SDKEvent
|
4
11
|
attr_reader :context, :rid, :event_type, :user_id, :user_traits, :request, :timestamp, :properties
|
5
12
|
attr_writer :context, :rid, :event_type, :user_id, :user_traits, :request, :timestamp, :properties
|
6
13
|
|
7
14
|
def initialize(event_options, securenative_options)
|
15
|
+
if event_options.user_id.nil? || event_options.user_id.length <= 0 || event_options.user_id == ''
|
16
|
+
raise SecureNativeInvalidOptionsError.new, 'Invalid event structure; User Id is missing'
|
17
|
+
end
|
18
|
+
|
19
|
+
if event_options.event.nil? || event_options.event.length <= 0 || event_options.event == ''
|
20
|
+
raise SecureNativeInvalidOptionsError.new, 'Invalid event structure; Event Type is missing'
|
21
|
+
end
|
22
|
+
|
8
23
|
@context = if !event_options.context.nil?
|
9
24
|
event_options.context
|
10
25
|
else
|
11
|
-
|
26
|
+
SecureNativeContext.default_context_builder
|
12
27
|
end
|
13
28
|
|
14
29
|
client_token = EncryptionUtils.decrypt(@context.client_token, securenative_options.api_key)
|
@@ -17,10 +32,11 @@ class SDKEvent
|
|
17
32
|
@event_type = event_options.event
|
18
33
|
@user_id = event_options.user_id
|
19
34
|
@user_traits = event_options.user_traits
|
20
|
-
@request = RequestContext(cid
|
21
|
-
|
22
|
-
|
23
|
-
|
35
|
+
@request = RequestContext.new(cid: client_token ? client_token.cid : '', vid: client_token ? client_token.vid : '',
|
36
|
+
fp: client_token ? client_token.fp : '', ip: @context.ip,
|
37
|
+
remote_ip: @context.remote_ip, headers: @context.headers,
|
38
|
+
url: @context.url, http_method: @context.http_method)
|
39
|
+
|
24
40
|
|
25
41
|
@timestamp = DateUtils.to_timestamp(event_options.timestamp)
|
26
42
|
@properties = event_options.properties
|
@@ -28,6 +44,6 @@ class SDKEvent
|
|
28
44
|
|
29
45
|
def to_s
|
30
46
|
"context: #{@context}, rid: #{@rid}, event_type: #{@event_type}, user_id: #{@user_id},
|
31
|
-
user_traits: #{@user_traits}, request: #{@request}, timestamp: #{@timestamp}, properties: #{@properties}"
|
47
|
+
user_traits: #{@user_traits}, request: #{@request}, timestamp: #{@timestamp}, properties: #{@properties}"
|
32
48
|
end
|
33
49
|
end
|