leanplum_api 3.1.0 → 4.0.0

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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +63 -32
  3. data/lib/leanplum_api/api.rb +107 -156
  4. data/lib/leanplum_api/configuration.rb +9 -1
  5. data/lib/leanplum_api/connection.rb +2 -2
  6. data/lib/leanplum_api/data_export_api.rb +78 -0
  7. data/lib/leanplum_api/faraday_middleware/response_validation.rb +23 -19
  8. data/lib/leanplum_api/version.rb +1 -1
  9. data/spec/api_spec.rb +142 -98
  10. data/spec/configuration_spec.rb +2 -2
  11. data/spec/data_export_api_spec.rb +57 -0
  12. data/spec/fixtures/vcr/delete_user.yml +129 -0
  13. data/spec/fixtures/vcr/export_data.yml +5 -5
  14. data/spec/fixtures/vcr/export_data_dates.yml +6 -6
  15. data/spec/fixtures/vcr/export_user.yml +7 -7
  16. data/spec/fixtures/vcr/export_users.yml +44 -0
  17. data/spec/fixtures/vcr/get_ab_test.yml +5 -5
  18. data/spec/fixtures/vcr/get_ab_tests.yml +5 -5
  19. data/spec/fixtures/vcr/get_export_results.yml +5 -5
  20. data/spec/fixtures/vcr/get_messages.yml +5 -5
  21. data/spec/fixtures/vcr/get_vars.yml +5 -5
  22. data/spec/fixtures/vcr/missing_message.yml +4 -4
  23. data/spec/fixtures/vcr/reset_anomalous_user.yml +6 -6
  24. data/spec/fixtures/vcr/set_device_attributes.yml +46 -0
  25. data/spec/fixtures/vcr/set_user_attributes.yml +7 -7
  26. data/spec/fixtures/vcr/set_user_attributes_with_devices.yml +46 -0
  27. data/spec/fixtures/vcr/set_user_attributes_with_devices_and_events.yml +46 -0
  28. data/spec/fixtures/vcr/set_user_attributes_with_events.yml +46 -0
  29. data/spec/fixtures/vcr/track_events.yml +8 -8
  30. data/spec/fixtures/vcr/track_events_and_attributes.yml +9 -9
  31. data/spec/fixtures/vcr/track_events_anomaly_overrider.yml +20 -19
  32. data/spec/fixtures/vcr/track_offline_events.yml +8 -8
  33. data/spec/http_spec.rb +6 -5
  34. data/spec/spec_helper.rb +11 -8
  35. metadata +40 -3
@@ -29,6 +29,9 @@ module LeanplumApi
29
29
  attr_accessor :logger
30
30
  attr_accessor :timeout_seconds
31
31
 
32
+ # Override validations for leanplum response. On by default.
33
+ attr_accessor :validate_response
34
+
32
35
  # Optional configuration for exporting raw data to S3.
33
36
  # If s3_bucket_name is provided, s3_access_id and s3_access_key must also be provided.
34
37
  attr_accessor :s3_bucket_name
@@ -39,9 +42,14 @@ module LeanplumApi
39
42
  def initialize
40
43
  @api_version = DEFAULT_LEANPLUM_API_VERSION
41
44
  @developer_mode = false
45
+ @validate_response = true
42
46
  @timeout_seconds = 600
43
47
  @logger = LeanplumApi::Logger.new(STDOUT)
44
- @api_debug = ENV['LEANPLUM_API_DEBUG'].to_s =~ /^(true|t|yes|y|1)$/i
48
+ @api_debug = debug_mode?
49
+ end
50
+
51
+ def debug_mode?
52
+ ENV['LEANPLUM_API_DEBUG'].to_s =~ /^(true|t|yes|y|1)$/i
45
53
  end
46
54
  end
47
55
  end
@@ -7,13 +7,13 @@ module LeanplumApi
7
7
  end
8
8
 
9
9
  def get(query)
10
- connection.get(LEANPLUM_API_PATH, query.merge(authentication_params))
10
+ connection.get(LEANPLUM_API_PATH, query.merge(authentication_params)).body['response']
11
11
  end
12
12
 
13
13
  def multi(payload)
14
14
  connection.post("#{LEANPLUM_API_PATH}?#{authed_multi_param_string}") do |request|
15
15
  request.body = { data: payload }
16
- end
16
+ end.body['response']
17
17
  end
18
18
 
19
19
  private
@@ -0,0 +1,78 @@
1
+ require 'leanplum_api/api'
2
+
3
+ # Support for data export features are semi-deprecated in the gem, because the data they give has historically
4
+ # been inaccurate. The automated S3 export has better accuracy with a fraction of the headaches.
5
+ # Use these methods at your own risk.
6
+
7
+ module LeanplumApi
8
+ class DataExportAPI < API
9
+ # Returns the jobId
10
+ # Leanplum has confirmed that using startTime and endTime, especially trying to be relatively up to the minute,
11
+ # leads to sort of unprocessed information that can be incomplete.
12
+ # They recommend using the automatic export to S3 if possible.
13
+ def export_data(start_time, end_time = nil)
14
+ LeanplumApi.configuration.logger.warn("You should probably use the direct S3 export instead of exportData")
15
+ fail "Start time #{start_time} after end time #{end_time}" if end_time && start_time > end_time
16
+ LeanplumApi.configuration.logger.info("Requesting data export from #{start_time} to #{end_time}...")
17
+
18
+ # Because of open questions about how startTime and endTime work (or don't work, as the case may be), we
19
+ # only want to pass the dates unless start and end times are specifically requested.
20
+ params = { action: 'exportData', startDate: start_time.strftime('%Y%m%d') }
21
+ params[:startTime] = start_time.strftime('%s') if start_time.is_a?(DateTime) || start_time.is_a?(Time)
22
+
23
+ if end_time
24
+ params[:endDate] = end_time.strftime('%Y%m%d')
25
+ params[:endTime] = end_time.strftime('%s') if end_time.is_a?(DateTime) || end_time.is_a?(Time)
26
+ end
27
+
28
+ # Handle optional S3 export params
29
+ if LeanplumApi.configuration.s3_bucket_name
30
+ fail 's3_bucket_name set but s3_access_id not configured!' unless LeanplumApi.configuration.s3_access_id
31
+ fail 's3_bucket_name set but s3_access_key not configured!' unless LeanplumApi.configuration.s3_access_key
32
+
33
+ params.merge!(
34
+ s3BucketName: LeanplumApi.configuration.s3_bucket_name,
35
+ s3AccessId: LeanplumApi.configuration.s3_access_id,
36
+ s3AccessKey: LeanplumApi.configuration.s3_access_key
37
+ )
38
+ params.merge!(s3ObjectPrefix: LeanplumApi.configuration.s3_object_prefix) if LeanplumApi.configuration.s3_object_prefix
39
+ end
40
+
41
+ data_export_connection.get(params).first['jobId']
42
+ end
43
+
44
+ def get_export_results(job_id)
45
+ response = data_export_connection.get(action: 'getExportResults', jobId: job_id).first
46
+
47
+ if response['state'] == EXPORT_FINISHED
48
+ LeanplumApi.configuration.logger.info("Export finished.")
49
+ LeanplumApi.configuration.logger.debug(" Response: #{response}")
50
+ {
51
+ files: response['files'],
52
+ number_of_sessions: response['numSessions'],
53
+ number_of_bytes: response['numBytes'],
54
+ state: response['state'],
55
+ s3_copy_status: response['s3CopyStatus']
56
+ }
57
+ else
58
+ { state: response['state'] }
59
+ end
60
+ end
61
+
62
+ # See leanplum docs.
63
+ # The segment syntax is identical to that produced by the "Insert Value" feature on the dashboard.
64
+ # Examples: 'Country = "US"', '{Country = "US"} and {App version = 1}'.
65
+ def export_users(ab_test_id = nil, segment = nil)
66
+ data_export_connection.get(action: 'exportUsers', segment: segment, ab_test_id: ab_test_id).first['jobId']
67
+ end
68
+
69
+ def wait_for_export_job(job_id, polling_interval = 60)
70
+ while get_export_results(job_id)[:state] != EXPORT_FINISHED
71
+ LeanplumApi.configuration.logger.debug("Polling job #{job_id}: #{get_export_results(job_id)}")
72
+ sleep(polling_interval)
73
+ end
74
+
75
+ get_export_results(job_id)
76
+ end
77
+ end
78
+ end
@@ -5,42 +5,46 @@ module LeanplumApi
5
5
  class ResponseValidation < Faraday::Middleware
6
6
  Faraday::Request.register_middleware(leanplum_response_validation: self)
7
7
 
8
- def call(environment)
9
- operations = nil
8
+ SUCCESS = 'success'.freeze
9
+ WARN = 'warning'.freeze
10
10
 
11
+ def call(environment)
11
12
  if environment.body
12
- operations = environment.body[:data] if environment.body[:data] && environment.body[:data].is_a?(Array)
13
+ requests = environment.body[:data] if environment.body[:data] && environment.body[:data].is_a?(Array)
13
14
  environment.body = environment.body.to_json
14
15
  end
15
16
 
16
17
  @app.call(environment).on_complete do |response|
17
18
  fail ResourceNotFoundError, response.inspect if response.status == 404
18
- fail BadResponseError, response.inspect unless response.status == 200 && response.body['response']
19
- fail BadResponseError, "No :success key in #{response.inspect}!" unless response.body['response'].is_a?(Array) && response.body['response'].first.has_key?('success')
20
- fail BadResponseError, "Not a success! Response: #{response.inspect}" unless response.body['response'].first['success'] == true
19
+ fail BadResponseError, response.inspect unless response.status == 200 && (responses = response.body['response']).is_a?(Array)
20
+ fail BadResponseError, "No :success key in #{responses.inspect}!" unless responses.all? { |r| r.key?(SUCCESS) }
21
21
 
22
- validate_operation_success(operations, response) if operations
22
+ validate_request_success(responses, requests) if LeanplumApi.configuration.validate_response
23
23
  end
24
24
  end
25
25
 
26
26
  private
27
27
 
28
- def validate_operation_success(operations, response)
29
- success_indicators = response.body['response']
30
- if success_indicators.size != operations.size
31
- fail "Attempted to do #{operations.size} operations but only received confirmation for #{success_indicators.size}!"
28
+ def validate_request_success(success_indicators, requests)
29
+ if requests && success_indicators.size != requests.size
30
+ fail BadResponseError, "Attempted #{requests.size} operations but received confirmation for #{success_indicators.size}!"
32
31
  end
33
32
 
34
- failures = []
35
- success_indicators.each_with_index do |s, i|
36
- if s['success'].to_s != 'true'
37
- LeanplumApi.configuration.logger.error("Unsuccessful request at position #{i}: #{operations[i]}")
38
- failures << { operation: operations[i], error: s }
33
+ failures = success_indicators.map.with_index do |indicator, i|
34
+ if indicator[WARN]
35
+ LeanplumApi.configuration.logger.warn((requests ? "Warning for #{requests[i]}: " : '') + indicator[WARN].to_s)
39
36
  end
40
- LeanplumApi.configuration.logger.warn("Warning for operation #{operations[i]}: #{s['warning']}") if s['warning']
41
- end
42
37
 
43
- fail LeanplumValidationException.new("Operation failures: #{failures}") if failures.size > 0
38
+ next nil if indicator[SUCCESS].to_s == 'true'
39
+
40
+ requests ? { operation: requests[i], error: indicator } : { error: indicator }
41
+ end.compact
42
+
43
+ unless failures.empty?
44
+ error_message = "Operation failures: #{failures}"
45
+ LeanplumApi.configuration.logger.error(error_message)
46
+ fail BadResponseError, error_message
47
+ end
44
48
  end
45
49
  end
46
50
  end
@@ -1,3 +1,3 @@
1
1
  module LeanplumApi
2
- VERSION = '3.1.0'
2
+ VERSION = '4.0.0'
3
3
  end
data/spec/api_spec.rb CHANGED
@@ -1,59 +1,109 @@
1
- require 'spec_helper'
2
-
3
1
  describe LeanplumApi::API do
4
2
  let(:api) { described_class.new }
5
3
  let(:first_user_id) { 123456 }
6
4
  let(:first_event_time) { Time.now.utc - 1.day }
7
5
  let(:last_event_time) { Time.now.utc }
8
- let(:users) do
9
- [{
6
+ let(:users) { [user] }
7
+ let(:devices) { [device] }
8
+ let(:user) do
9
+ {
10
10
  user_id: first_user_id,
11
11
  first_name: 'Mike',
12
12
  last_name: 'Jones',
13
13
  gender: 'm',
14
14
  email: 'still_tippin@test.com',
15
15
  create_date: '2010-01-01'.to_date,
16
- is_tipping: true,
17
- events: {
18
- eventName1: {
19
- count: 1,
20
- firstTime: first_event_time,
21
- lastTime: last_event_time
22
- }
23
- }
24
- }]
16
+ is_tipping: true
17
+ }
18
+ end
19
+ let(:device) do
20
+ {
21
+ device_id: 'fu123',
22
+ appVersion: 'x42x',
23
+ deviceModel: 'p0d',
24
+ create_date: '2018-01-01'.to_date
25
+ }
26
+ end
27
+
28
+ context 'devices' do
29
+ it 'build_device_attributes_hash' do
30
+ expect(api.send(:build_device_attributes_hash, device)).to eq(
31
+ deviceId: device[:device_id],
32
+ action: described_class::SET_DEVICE_ATTRIBUTES,
33
+ deviceAttributes: api.send(:fix_iso8601, device.except(:device_id))
34
+ )
35
+ end
36
+
37
+ context 'set_device_attributes' do
38
+ it 'sets device attributes without error' do
39
+ VCR.use_cassette('set_device_attributes') do
40
+ expect { api.set_device_attributes(devices) }.to_not raise_error
41
+ end
42
+ end
43
+ end
25
44
  end
26
45
 
27
46
  context 'users' do
28
- it 'build_user_attributes_hash' do
29
- expect(api.send(:build_user_attributes_hash, users.first)).to eq({
30
- userId: first_user_id,
31
- action: 'setUserAttributes',
32
- events: {
33
- 'eventName1' => {
34
- 'count' => 1,
35
- 'firstTime' => first_event_time.strftime('%s').to_i,
36
- 'lastTime' => last_event_time.strftime('%s').to_i
37
- }
38
- },
39
- userAttributes: HashWithIndifferentAccess.new(
40
- first_name: 'Mike',
41
- last_name: 'Jones',
42
- gender: 'm',
43
- email: 'still_tippin@test.com',
44
- create_date: '2010-01-01',
45
- is_tipping: true
46
- )
47
- })
47
+ let(:events) { { eventName1: { count: 1, firstTime: first_event_time, lastTime: last_event_time } } }
48
+ let(:events_with_timestamps) { Hash[events.map { |k, v| [k, api.send(:fix_seconds_since_epoch, v)] }] }
49
+ let(:user_with_devices) { user.merge(devices: devices) }
50
+ let(:user_with_events) { user.merge(events: events) }
51
+
52
+ context '#build_user_attributes_hash' do
53
+ let(:built_attributes) do
54
+ {
55
+ userId: first_user_id,
56
+ action: described_class::SET_USER_ATTRIBUTES,
57
+ userAttributes: api.send(:fix_iso8601, user.except(:user_id))
58
+ }
59
+ end
60
+
61
+ it 'builds the right hash' do
62
+ expect(api.send(:build_user_attributes_hash, user)).to eq(built_attributes)
63
+ end
64
+
65
+ context 'with events' do
66
+ it 'builds the right hash' do
67
+ expect(api.send(:build_user_attributes_hash, user_with_events)).to eq(
68
+ built_attributes.merge(events: events_with_timestamps)
69
+ )
70
+ end
71
+ end
72
+
73
+ context 'with devices' do
74
+ it 'builds the right hash' do
75
+ expect(api.send(:build_user_attributes_hash, user_with_devices)).to eq(
76
+ built_attributes.merge(devices: devices)
77
+ )
78
+ end
79
+ end
48
80
  end
49
81
 
50
- context 'set_user_attributes' do
82
+ context '#set_user_attributes' do
51
83
  context 'valid request' do
52
84
  it 'should successfully set user attributes' do
53
85
  VCR.use_cassette('set_user_attributes') do
54
86
  expect { api.set_user_attributes(users) }.to_not raise_error
55
87
  end
56
88
  end
89
+
90
+ it 'should successfully set user attributes and events' do
91
+ VCR.use_cassette('set_user_attributes_with_events') do
92
+ expect { api.set_user_attributes([user_with_events]) }.to_not raise_error
93
+ end
94
+ end
95
+
96
+ it 'should successfully set user attributes and devices' do
97
+ VCR.use_cassette('set_user_attributes_with_devices') do
98
+ expect { api.set_user_attributes([user_with_devices]) }.to_not raise_error
99
+ end
100
+ end
101
+
102
+ it 'should successfully set user attributes and devices and events' do
103
+ VCR.use_cassette('set_user_attributes_with_devices_and_events') do
104
+ expect { api.set_user_attributes([user_with_devices.merge(events: events)]) }.to_not raise_error
105
+ end
106
+ end
57
107
  end
58
108
 
59
109
  context 'invalid request' do
@@ -65,34 +115,43 @@ describe LeanplumApi::API do
65
115
  end
66
116
  end
67
117
 
68
- context 'user_attributes' do
118
+ context '#user_attributes' do
69
119
  it 'should get user attributes for this user' do
70
120
  VCR.use_cassette('export_user') do
71
121
  api.user_attributes(first_user_id).each do |k, v|
72
- if users.first[k.to_sym].is_a?(Date) || users.first[k.to_sym].is_a?(DateTime)
73
- expect(v).to eq(users.first[k.to_sym].strftime('%Y-%m-%d'))
122
+ if user[k.to_sym].is_a?(Date) || user[k.to_sym].is_a?(DateTime)
123
+ expect(v).to eq(user[k.to_sym].strftime('%Y-%m-%d'))
74
124
  else
75
- expect(v).to eq(users.first[k.to_sym])
125
+ expect(v).to eq(user[k.to_sym])
76
126
  end
77
127
  end
78
128
  end
79
129
  end
80
130
  end
81
131
 
82
- context 'export_users' do
83
- it 'should export users'
84
- end
85
-
86
- context 'reset_anomalous_users' do
132
+ context '#reset_anomalous_users' do
87
133
  it 'should successfully call setUserAttributes with resetAnomalies' do
88
134
  VCR.use_cassette('reset_anomalous_user') do
89
135
  expect { api.reset_anomalous_users(first_user_id) }.to_not raise_error
90
136
  end
91
137
  end
92
138
  end
139
+
140
+ context '#delete_user' do
141
+ let(:user_id) { 'delete_yourself_123' }
142
+ let(:deletable_user) { user.merge(user_id: user_id) }
143
+
144
+ it 'should delete a user' do
145
+ VCR.use_cassette('delete_user') do
146
+ expect { api.set_user_attributes(deletable_user) }.to_not raise_error
147
+ expect { api.delete_user(user_id) }.to_not raise_error
148
+ expect { api.user_attributes(user_id) }.to raise_error(LeanplumApi::ResourceNotFoundError)
149
+ end
150
+ end
151
+ end
93
152
  end
94
153
 
95
- context 'events' do
154
+ context 'event tracking' do
96
155
  let(:timestamp) { '2015-05-01 01:02:03' }
97
156
  let(:purchase) { 'purchase' }
98
157
  let(:events) do
@@ -100,13 +159,13 @@ describe LeanplumApi::API do
100
159
  {
101
160
  user_id: first_user_id,
102
161
  event: purchase,
103
- time: Time.now.utc,
162
+ time: last_event_time,
104
163
  some_timestamp: timestamp
105
164
  },
106
165
  {
107
166
  user_id: 54321,
108
167
  event: 'purchase_page_view',
109
- time: Time.now.utc - 10.minutes
168
+ time: last_event_time - 10.minutes
110
169
  }
111
170
  ]
112
171
  end
@@ -115,9 +174,9 @@ describe LeanplumApi::API do
115
174
  let(:event_hash) do
116
175
  {
117
176
  userId: first_user_id,
118
- time: Time.now.utc.strftime('%s').to_i,
119
- action: 'track',
120
177
  event: purchase,
178
+ time: last_event_time.strftime('%s').to_i,
179
+ action: described_class::TRACK,
121
180
  params: { some_timestamp: timestamp }
122
181
  }
123
182
  end
@@ -127,7 +186,7 @@ describe LeanplumApi::API do
127
186
  end
128
187
  end
129
188
 
130
- context 'without user attributes' do
189
+ context '#track_events' do
131
190
  context 'valid request' do
132
191
  it 'should successfully track session events' do
133
192
  VCR.use_cassette('track_events') do
@@ -137,7 +196,10 @@ describe LeanplumApi::API do
137
196
 
138
197
  it 'should successfully track non session events' do
139
198
  VCR.use_cassette('track_offline_events') do
140
- expect { api.track_events(events, allow_offline: true) }.to_not raise_error
199
+ expect do
200
+ response = api.track_events(events, allow_offline: true)
201
+ expect(response.map { |r| r['success'] && r['isOffline'] }.all?).to be_truthy
202
+ end.to_not raise_error
141
203
  end
142
204
  end
143
205
  end
@@ -153,28 +215,34 @@ describe LeanplumApi::API do
153
215
  end
154
216
 
155
217
  context 'anomalous data force_anomalous_override' do
156
- let(:old_events) { events.map { |e| e[:time] -= 1.year; e } }
218
+ let(:old_events) { events.map { |e| e[:time] -= 2.years; e } }
157
219
 
158
220
  it 'should successfully force the anomalous data override events' do
159
221
  VCR.use_cassette('track_events_anomaly_overrider') do
160
- expect { api.track_events(old_events, force_anomalous_override: true) }.to_not raise_error
222
+ expect do
223
+ response = api.track_events(old_events, force_anomalous_override: true)
224
+ expect(response.map { |r| r['warning']['message'] }.all? { |w| w =~ /Past event detected/ }).to be true
225
+ end.to_not raise_error
161
226
  end
162
227
  end
163
228
  end
164
229
  end
165
230
 
166
- context 'along with user attributes' do
167
- it 'should work' do
231
+ context '#track_multi' do
232
+ it 'tracks users and events at the same time' do
168
233
  VCR.use_cassette('track_events_and_attributes') do
169
- expect { api.track_multi(events, users) }.to_not raise_error
234
+ expect do
235
+ response = api.track_multi(events: events, user_attributes: users)
236
+ expect(response.first['success']).to be true
237
+ end.to_not raise_error
170
238
  end
171
239
  end
172
240
  end
173
241
 
174
- context 'user_events' do
242
+ context '#user_events' do
175
243
  it 'should get user events for this user' do
176
244
  VCR.use_cassette('export_user') do
177
- expect(api.user_events(first_user_id)[purchase].keys).to eq(%w(firstTime lastTime count))
245
+ expect(api.user_events(first_user_id)[purchase].keys.sort).to eq(%w(firstTime lastTime count).sort)
178
246
  end
179
247
  end
180
248
  end
@@ -188,46 +256,6 @@ describe LeanplumApi::API do
188
256
  LeanplumApi.configure { |c| c.developer_mode = true }
189
257
  end
190
258
 
191
- context 'data export methods' do
192
- context 'export_data' do
193
- context 'regular export' do
194
- it 'should request a data export job with a starttime' do
195
- VCR.use_cassette('export_data') do
196
- expect { api.export_data(Time.at(1438660800).utc) }.to raise_error LeanplumApi::BadResponseError
197
- end
198
- end
199
-
200
- it 'should request a data export job with start and end dates' do
201
- VCR.use_cassette('export_data_dates') do
202
- expect { api.export_data(Date.new(2017, 8, 5), Date.new(2017, 8, 6)) }.to_not raise_error
203
- end
204
- end
205
- end
206
-
207
- context 's3 export' do
208
- let(:s3_bucket_name) { 'bucket' }
209
- let(:s3_access_key) { 's3_access_key' }
210
- let(:s3_access_id) { 's3_access_id' }
211
-
212
- it 'should request an S3 export'
213
- end
214
- end
215
-
216
- context 'get_export_results' do
217
- it 'should get a status for a data export job' do
218
- VCR.use_cassette('get_export_results') do
219
- expect(api.get_export_results('export_4727756026281984_2904941266315269120')).to eq({
220
- files: ['https://leanplum_export.storage.googleapis.com/export-4727756026281984-d5969d55-f242-48a6-85a3-165af08e2306-output-0'],
221
- number_of_bytes: 36590,
222
- number_of_sessions: 101,
223
- state: LeanplumApi::API::EXPORT_FINISHED,
224
- s3_copy_status: nil
225
- })
226
- end
227
- end
228
- end
229
- end
230
-
231
259
  context 'content read only methods' do
232
260
  context 'ab tests' do
233
261
  it 'gets ab tests' do
@@ -267,9 +295,25 @@ describe LeanplumApi::API do
267
295
  pending 'Docs are extremely unclear about what getVars and setVars even do'
268
296
 
269
297
  VCR.use_cassette('get_vars') do
270
- expect(api.get_vars(users.first[:user_id])).to eq({ 'test_var' => 1 })
298
+ expect(api.get_vars(user[:user_id])).to eq({ 'test_var' => 1 })
271
299
  end
272
300
  end
273
301
  end
274
302
  end
303
+
304
+ context 'hash utility methods' do
305
+ let(:hash_with_times) { { not_time: 'grippin', time: Time.now.utc, date: Time.now.utc.to_date } }
306
+
307
+ it 'turns datetimes into seconds from the epoch' do
308
+ expect(api.send(:fix_seconds_since_epoch, hash_with_times)).to eq(
309
+ hash_with_times.merge(time: Time.now.utc.strftime('%s').to_i, date: Time.now.utc.to_date.strftime('%s').to_i)
310
+ )
311
+ end
312
+
313
+ it 'turns datetimes into iso8601 format' do
314
+ expect(api.send(:fix_iso8601, hash_with_times)).to eq(
315
+ hash_with_times.merge(time: Time.now.utc.iso8601, date: Time.now.utc.to_date.iso8601)
316
+ )
317
+ end
318
+ end
275
319
  end