astronomer 2.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/History.md +160 -0
- data/Makefile +8 -0
- data/README.md +91 -0
- data/Rakefile +7 -0
- data/analytics-ruby.gemspec +23 -0
- data/astronomer-2.0.13.gem +0 -0
- data/lib/segment.rb +1 -0
- data/lib/segment/analytics.rb +31 -0
- data/lib/segment/analytics/client.rb +359 -0
- data/lib/segment/analytics/defaults.rb +20 -0
- data/lib/segment/analytics/logging.rb +35 -0
- data/lib/segment/analytics/request.rb +82 -0
- data/lib/segment/analytics/response.rb +16 -0
- data/lib/segment/analytics/utils.rb +88 -0
- data/lib/segment/analytics/version.rb +5 -0
- data/lib/segment/analytics/worker.rb +60 -0
- data/spec/segment/analytics/client_spec.rb +302 -0
- data/spec/segment/analytics/request_spec.rb +191 -0
- data/spec/segment/analytics/response_spec.rb +30 -0
- data/spec/segment/analytics/worker_spec.rb +96 -0
- data/spec/segment/analytics_spec.rb +106 -0
- data/spec/spec_helper.rb +81 -0
- metadata +143 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module Segment
|
2
|
+
class Analytics
|
3
|
+
module Defaults
|
4
|
+
module Request
|
5
|
+
HOST = 'api.astronomer.io'
|
6
|
+
PORT = 443
|
7
|
+
PATH = '/v1/import'
|
8
|
+
SSL = true
|
9
|
+
HEADERS = { :accept => 'application/json' }
|
10
|
+
RETRIES = 4
|
11
|
+
BACKOFF = 30.0
|
12
|
+
end
|
13
|
+
|
14
|
+
module Queue
|
15
|
+
BATCH_SIZE = 100
|
16
|
+
MAX_SIZE = 10000
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Segment
|
4
|
+
class Analytics
|
5
|
+
module Logging
|
6
|
+
class << self
|
7
|
+
def logger
|
8
|
+
@logger ||= if defined?(Rails)
|
9
|
+
Rails.logger
|
10
|
+
else
|
11
|
+
logger = Logger.new STDOUT
|
12
|
+
logger.progname = 'Segment::Analytics'
|
13
|
+
logger
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def logger= logger
|
18
|
+
@logger = logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included base
|
23
|
+
class << base
|
24
|
+
def logger
|
25
|
+
Logging.logger
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def logger
|
31
|
+
Logging.logger
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'segment/analytics/defaults'
|
2
|
+
require 'segment/analytics/utils'
|
3
|
+
require 'segment/analytics/response'
|
4
|
+
require 'segment/analytics/logging'
|
5
|
+
require 'net/http'
|
6
|
+
require 'net/https'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
module Segment
|
10
|
+
class Analytics
|
11
|
+
class Request
|
12
|
+
include Segment::Analytics::Defaults::Request
|
13
|
+
include Segment::Analytics::Utils
|
14
|
+
include Segment::Analytics::Logging
|
15
|
+
|
16
|
+
# public: Creates a new request object to send analytics batch
|
17
|
+
#
|
18
|
+
def initialize(options = {})
|
19
|
+
options[:host] ||= HOST
|
20
|
+
options[:port] ||= PORT
|
21
|
+
options[:ssl] ||= SSL
|
22
|
+
options[:headers] ||= HEADERS
|
23
|
+
@path = options[:path] || PATH
|
24
|
+
@retries = options[:retries] || RETRIES
|
25
|
+
@backoff = options[:backoff] || BACKOFF
|
26
|
+
|
27
|
+
http = Net::HTTP.new(options[:host], options[:port])
|
28
|
+
http.use_ssl = options[:ssl]
|
29
|
+
http.read_timeout = 8
|
30
|
+
http.open_timeout = 4
|
31
|
+
|
32
|
+
@http = http
|
33
|
+
end
|
34
|
+
|
35
|
+
# public: Posts the app_id and batch of messages to the API.
|
36
|
+
#
|
37
|
+
# returns - Response of the status and error if it exists
|
38
|
+
def post(app_id, batch)
|
39
|
+
status, error = nil, nil
|
40
|
+
remaining_retries = @retries
|
41
|
+
backoff = @backoff
|
42
|
+
headers = { 'Content-Type' => 'application/json', 'accept' => 'application/json' }
|
43
|
+
begin
|
44
|
+
payload = JSON.generate :sentAt => datetime_in_iso8601(Time.new), :batch => batch
|
45
|
+
request = Net::HTTP::Post.new(@path, headers)
|
46
|
+
request.basic_auth app_id, nil
|
47
|
+
|
48
|
+
if self.class.stub
|
49
|
+
status = 200
|
50
|
+
error = nil
|
51
|
+
logger.debug "stubbed request to #{@path}: app id = #{app_id}, payload = #{payload}"
|
52
|
+
else
|
53
|
+
res = @http.request(request, payload)
|
54
|
+
status = res.code.to_i
|
55
|
+
body = JSON.parse(res.body)
|
56
|
+
error = body["error"]
|
57
|
+
end
|
58
|
+
rescue Exception => e
|
59
|
+
unless (remaining_retries -=1).zero?
|
60
|
+
sleep(backoff)
|
61
|
+
retry
|
62
|
+
end
|
63
|
+
|
64
|
+
logger.error e.message
|
65
|
+
e.backtrace.each { |line| logger.error line }
|
66
|
+
status = -1
|
67
|
+
error = "Connection error: #{e}"
|
68
|
+
end
|
69
|
+
|
70
|
+
Response.new status, error
|
71
|
+
end
|
72
|
+
|
73
|
+
class << self
|
74
|
+
attr_accessor :stub
|
75
|
+
|
76
|
+
def stub
|
77
|
+
@stub || ENV['STUB']
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Segment
|
2
|
+
class Analytics
|
3
|
+
class Response
|
4
|
+
attr_reader :status, :error
|
5
|
+
|
6
|
+
# public: Simple class to wrap responses from the API
|
7
|
+
#
|
8
|
+
#
|
9
|
+
def initialize(status = 200, error = nil)
|
10
|
+
@status = status
|
11
|
+
@error = error
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Segment
|
4
|
+
class Analytics
|
5
|
+
module Utils
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# public: Return a new hash with keys converted from strings to symbols
|
9
|
+
#
|
10
|
+
def symbolize_keys(hash)
|
11
|
+
hash.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
|
12
|
+
end
|
13
|
+
|
14
|
+
# public: Convert hash keys from strings to symbols in place
|
15
|
+
#
|
16
|
+
def symbolize_keys!(hash)
|
17
|
+
hash.replace symbolize_keys hash
|
18
|
+
end
|
19
|
+
|
20
|
+
# public: Return a new hash with keys as strings
|
21
|
+
#
|
22
|
+
def stringify_keys(hash)
|
23
|
+
hash.inject({}) { |memo, (k,v)| memo[k.to_s] = v; memo }
|
24
|
+
end
|
25
|
+
|
26
|
+
# public: Returns a new hash with all the date values in the into iso8601
|
27
|
+
# strings
|
28
|
+
#
|
29
|
+
def isoify_dates(hash)
|
30
|
+
hash.inject({}) { |memo, (k, v)|
|
31
|
+
memo[k] = datetime_in_iso8601(v)
|
32
|
+
memo
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
# public: Converts all the date values in the into iso8601 strings in place
|
37
|
+
#
|
38
|
+
def isoify_dates!(hash)
|
39
|
+
hash.replace isoify_dates hash
|
40
|
+
end
|
41
|
+
|
42
|
+
# public: Returns a uid string
|
43
|
+
#
|
44
|
+
def uid
|
45
|
+
arr = SecureRandom.random_bytes(16).unpack("NnnnnN")
|
46
|
+
arr[2] = (arr[2] & 0x0fff) | 0x4000
|
47
|
+
arr[3] = (arr[3] & 0x3fff) | 0x8000
|
48
|
+
"%08x-%04x-%04x-%04x-%04x%08x" % arr
|
49
|
+
end
|
50
|
+
|
51
|
+
def datetime_in_iso8601 datetime
|
52
|
+
case datetime
|
53
|
+
when Time
|
54
|
+
time_in_iso8601 datetime
|
55
|
+
when DateTime
|
56
|
+
time_in_iso8601 datetime.to_time
|
57
|
+
when Date
|
58
|
+
date_in_iso8601 datetime
|
59
|
+
else
|
60
|
+
datetime
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def time_in_iso8601 time, fraction_digits = 3
|
65
|
+
fraction = if fraction_digits > 0
|
66
|
+
(".%06i" % time.usec)[0, fraction_digits + 1]
|
67
|
+
end
|
68
|
+
|
69
|
+
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(time, true, 'Z')}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def date_in_iso8601 date
|
73
|
+
date.strftime("%F")
|
74
|
+
end
|
75
|
+
|
76
|
+
def formatted_offset time, colon = true, alternate_utc_string = nil
|
77
|
+
time.utc? && alternate_utc_string || seconds_to_utc_offset(time.utc_offset, colon)
|
78
|
+
end
|
79
|
+
|
80
|
+
def seconds_to_utc_offset(seconds, colon = true)
|
81
|
+
(colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON) % [(seconds < 0 ? '-' : '+'), (seconds.abs / 3600), ((seconds.abs % 3600) / 60)]
|
82
|
+
end
|
83
|
+
|
84
|
+
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
|
85
|
+
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'segment/analytics/defaults'
|
2
|
+
require 'segment/analytics/utils'
|
3
|
+
require 'segment/analytics/defaults'
|
4
|
+
require 'segment/analytics/request'
|
5
|
+
|
6
|
+
module Segment
|
7
|
+
class Analytics
|
8
|
+
class Worker
|
9
|
+
include Segment::Analytics::Utils
|
10
|
+
include Segment::Analytics::Defaults
|
11
|
+
|
12
|
+
# public: Creates a new worker
|
13
|
+
#
|
14
|
+
# The worker continuously takes messages off the queue
|
15
|
+
# and makes requests to the segment.io api
|
16
|
+
#
|
17
|
+
# queue - Queue synchronized between client and worker
|
18
|
+
# app_id - String of the project's app_id
|
19
|
+
# options - Hash of worker options
|
20
|
+
# batch_size - Fixnum of how many items to send in a batch
|
21
|
+
# on_error - Proc of what to do on an error
|
22
|
+
#
|
23
|
+
def initialize(queue, app_id, options = {})
|
24
|
+
symbolize_keys! options
|
25
|
+
@queue = queue
|
26
|
+
@app_id = app_id
|
27
|
+
@batch_size = options[:batch_size] || Queue::BATCH_SIZE
|
28
|
+
@on_error = options[:on_error] || Proc.new { |status, error| }
|
29
|
+
@batch = []
|
30
|
+
@lock = Mutex.new
|
31
|
+
end
|
32
|
+
|
33
|
+
# public: Continuously runs the loop to check for new events
|
34
|
+
#
|
35
|
+
def run
|
36
|
+
until Thread.current[:should_exit]
|
37
|
+
return if @queue.empty?
|
38
|
+
|
39
|
+
@lock.synchronize do
|
40
|
+
until @batch.length >= @batch_size || @queue.empty?
|
41
|
+
@batch << @queue.pop
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
res = Request.new.post @app_id, @batch
|
46
|
+
|
47
|
+
@lock.synchronize { @batch.clear }
|
48
|
+
|
49
|
+
@on_error.call res.status, res.error unless res.status == 200
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# public: Check whether we have outstanding requests.
|
54
|
+
#
|
55
|
+
def is_requesting?
|
56
|
+
@lock.synchronize { !@batch.empty? }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Segment
|
4
|
+
class Analytics
|
5
|
+
describe Client do
|
6
|
+
let(:client) { Client.new :write_key => WRITE_KEY }
|
7
|
+
let(:queue) { client.instance_variable_get :@queue }
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
it 'errors if no write_key is supplied' do
|
11
|
+
expect { Client.new }.to raise_error(ArgumentError)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'does not error if a write_key is supplied' do
|
15
|
+
expect do
|
16
|
+
Client.new :write_key => WRITE_KEY
|
17
|
+
end.to_not raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'does not error if a write_key is supplied as a string' do
|
21
|
+
expect do
|
22
|
+
Client.new 'write_key' => WRITE_KEY
|
23
|
+
end.to_not raise_error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#track' do
|
28
|
+
it 'errors without an event' do
|
29
|
+
expect { client.track(:user_id => 'user') }.to raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'errors without a user_id' do
|
33
|
+
expect { client.track(:event => 'Event') }.to raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'errors if properties is not a hash' do
|
37
|
+
expect {
|
38
|
+
client.track({
|
39
|
+
:user_id => 'user',
|
40
|
+
:event => 'Event',
|
41
|
+
:properties => [1,2,3]
|
42
|
+
})
|
43
|
+
}.to raise_error(ArgumentError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'uses the timestamp given' do
|
47
|
+
time = Time.parse("1990-07-16 13:30:00.123 UTC")
|
48
|
+
|
49
|
+
client.track({
|
50
|
+
:event => 'testing the timestamp',
|
51
|
+
:user_id => 'joe',
|
52
|
+
:timestamp => time
|
53
|
+
})
|
54
|
+
|
55
|
+
msg = queue.pop
|
56
|
+
|
57
|
+
expect(Time.parse(msg[:timestamp])).to eq(time)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'does not error with the required options' do
|
61
|
+
expect do
|
62
|
+
client.track Queued::TRACK
|
63
|
+
queue.pop
|
64
|
+
end.to_not raise_error
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not error when given string keys' do
|
68
|
+
expect do
|
69
|
+
client.track Utils.stringify_keys(Queued::TRACK)
|
70
|
+
queue.pop
|
71
|
+
end.to_not raise_error
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'converts time and date traits into iso8601 format' do
|
75
|
+
client.track({
|
76
|
+
:user_id => 'user',
|
77
|
+
:event => 'Event',
|
78
|
+
:properties => {
|
79
|
+
:time => Time.utc(2013),
|
80
|
+
:time_with_zone => Time.zone.parse('2013-01-01'),
|
81
|
+
:date_time => DateTime.new(2013,1,1),
|
82
|
+
:date => Date.new(2013,1,1),
|
83
|
+
:nottime => 'x'
|
84
|
+
}
|
85
|
+
})
|
86
|
+
message = queue.pop
|
87
|
+
|
88
|
+
expect(message[:properties][:time]).to eq('2013-01-01T00:00:00.000Z')
|
89
|
+
expect(message[:properties][:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
|
90
|
+
expect(message[:properties][:date_time]).to eq('2013-01-01T00:00:00.000Z')
|
91
|
+
expect(message[:properties][:date]).to eq('2013-01-01')
|
92
|
+
expect(message[:properties][:nottime]).to eq('x')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
describe '#identify' do
|
98
|
+
it 'errors without any user id' do
|
99
|
+
expect { client.identify({}) }.to raise_error(ArgumentError)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'does not error with the required options' do
|
103
|
+
expect do
|
104
|
+
client.identify Queued::IDENTIFY
|
105
|
+
queue.pop
|
106
|
+
end.to_not raise_error
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'does not error with the required options as strings' do
|
110
|
+
expect do
|
111
|
+
client.identify Utils.stringify_keys(Queued::IDENTIFY)
|
112
|
+
queue.pop
|
113
|
+
end.to_not raise_error
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'converts time and date traits into iso8601 format' do
|
117
|
+
client.identify({
|
118
|
+
:user_id => 'user',
|
119
|
+
:traits => {
|
120
|
+
:time => Time.utc(2013),
|
121
|
+
:time_with_zone => Time.zone.parse('2013-01-01'),
|
122
|
+
:date_time => DateTime.new(2013,1,1),
|
123
|
+
:date => Date.new(2013,1,1),
|
124
|
+
:nottime => 'x'
|
125
|
+
}
|
126
|
+
})
|
127
|
+
|
128
|
+
message = queue.pop
|
129
|
+
|
130
|
+
expect(message[:traits][:time]).to eq('2013-01-01T00:00:00.000Z')
|
131
|
+
expect(message[:traits][:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
|
132
|
+
expect(message[:traits][:date_time]).to eq('2013-01-01T00:00:00.000Z')
|
133
|
+
expect(message[:traits][:date]).to eq('2013-01-01')
|
134
|
+
expect(message[:traits][:nottime]).to eq('x')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#alias' do
|
139
|
+
it 'errors without from' do
|
140
|
+
expect { client.alias :user_id => 1234 }.to raise_error(ArgumentError)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'errors without to' do
|
144
|
+
expect { client.alias :previous_id => 1234 }.to raise_error(ArgumentError)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'does not error with the required options' do
|
148
|
+
expect { client.alias ALIAS }.to_not raise_error
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'does not error with the required options as strings' do
|
152
|
+
expect do
|
153
|
+
client.alias Utils.stringify_keys(ALIAS)
|
154
|
+
end.to_not raise_error
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe '#group' do
|
159
|
+
after do
|
160
|
+
client.flush
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'errors without group_id' do
|
164
|
+
expect { client.group :user_id => 'foo' }.to raise_error(ArgumentError)
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'errors without user_id' do
|
168
|
+
expect { client.group :group_id => 'foo' }.to raise_error(ArgumentError)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'does not error with the required options' do
|
172
|
+
client.group Queued::GROUP
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'does not error with the required options as strings' do
|
176
|
+
client.group Utils.stringify_keys(Queued::GROUP)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'converts time and date traits into iso8601 format' do
|
180
|
+
client.identify({
|
181
|
+
:user_id => 'user',
|
182
|
+
:group_id => 'group',
|
183
|
+
:traits => {
|
184
|
+
:time => Time.utc(2013),
|
185
|
+
:time_with_zone => Time.zone.parse('2013-01-01'),
|
186
|
+
:date_time => DateTime.new(2013,1,1),
|
187
|
+
:date => Date.new(2013,1,1),
|
188
|
+
:nottime => 'x'
|
189
|
+
}
|
190
|
+
})
|
191
|
+
|
192
|
+
message = queue.pop
|
193
|
+
|
194
|
+
expect(message[:traits][:time]).to eq('2013-01-01T00:00:00.000Z')
|
195
|
+
expect(message[:traits][:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
|
196
|
+
expect(message[:traits][:date_time]).to eq('2013-01-01T00:00:00.000Z')
|
197
|
+
expect(message[:traits][:date]).to eq('2013-01-01')
|
198
|
+
expect(message[:traits][:nottime]).to eq('x')
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#page' do
|
203
|
+
it 'errors without user_id' do
|
204
|
+
expect { client.page :name => 'foo' }.to raise_error(ArgumentError)
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'does not error with the required options' do
|
208
|
+
expect { client.page Queued::PAGE }.to_not raise_error
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'does not error with the required options as strings' do
|
212
|
+
expect do
|
213
|
+
client.page Utils.stringify_keys(Queued::PAGE)
|
214
|
+
end.to_not raise_error
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe '#screen' do
|
219
|
+
it 'errors without user_id' do
|
220
|
+
expect { client.screen :name => 'foo' }.to raise_error(ArgumentError)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'does not error with the required options' do
|
224
|
+
expect { client.screen Queued::SCREEN }.to_not raise_error
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'does not error with the required options as strings' do
|
228
|
+
expect do
|
229
|
+
client.screen Utils.stringify_keys(Queued::SCREEN)
|
230
|
+
end.to_not raise_error
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe '#flush' do
|
235
|
+
it 'waits for the queue to finish on a flush' do
|
236
|
+
client.identify Queued::IDENTIFY
|
237
|
+
client.track Queued::TRACK
|
238
|
+
client.flush
|
239
|
+
|
240
|
+
expect(client.queued_messages).to eq(0)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'completes when the process forks' do
|
244
|
+
client.identify Queued::IDENTIFY
|
245
|
+
|
246
|
+
Process.fork do
|
247
|
+
client.track Queued::TRACK
|
248
|
+
client.flush
|
249
|
+
expect(client.queued_messages).to eq(0)
|
250
|
+
end
|
251
|
+
|
252
|
+
Process.wait
|
253
|
+
end unless defined? JRUBY_VERSION
|
254
|
+
end
|
255
|
+
|
256
|
+
context 'common' do
|
257
|
+
check_property = proc { |msg, k, v| msg[k] && msg[k] == v }
|
258
|
+
|
259
|
+
let(:data) { { :user_id => 1, :group_id => 2, :previous_id => 3, :anonymous_id => 4, :event => "coco barked", :name => "coco" } }
|
260
|
+
|
261
|
+
it 'does not convert ids given as fixnums to strings' do
|
262
|
+
[:track, :screen, :page, :identify].each do |s|
|
263
|
+
client.send(s, data)
|
264
|
+
message = queue.pop(true)
|
265
|
+
|
266
|
+
expect(check_property.call(message, :userId, 1)).to eq(true)
|
267
|
+
expect(check_property.call(message, :anonymousId, 4)).to eq(true)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context 'group' do
|
272
|
+
it 'does not convert ids given as fixnums to strings' do
|
273
|
+
client.group(data)
|
274
|
+
message = queue.pop(true)
|
275
|
+
|
276
|
+
expect(check_property.call(message, :userId, 1)).to eq(true)
|
277
|
+
expect(check_property.call(message, :groupId, 2)).to eq(true)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context 'alias' do
|
282
|
+
it 'does not convert ids given as fixnums to strings' do
|
283
|
+
client.alias(data)
|
284
|
+
message = queue.pop(true)
|
285
|
+
|
286
|
+
expect(check_property.call(message, :userId, 1)).to eq(true)
|
287
|
+
expect(check_property.call(message, :previousId, 3)).to eq(true)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'sends integrations' do
|
292
|
+
[:track, :screen, :page, :group, :identify, :alias].each do |s|
|
293
|
+
client.send s, :integrations => { :All => true, :Salesforce => false }, :user_id => 1, :group_id => 2, :previous_id => 3, :anonymous_id => 4, :event => "coco barked", :name => "coco"
|
294
|
+
message = queue.pop(true)
|
295
|
+
expect(message[:integrations][:All]).to eq(true)
|
296
|
+
expect(message[:integrations][:Salesforce]).to eq(false)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|