Skip to content
This repository has been archived by the owner on Feb 28, 2023. It is now read-only.

Commit

Permalink
Merge d74acb9 into 0f0a353
Browse files Browse the repository at this point in the history
  • Loading branch information
macobo committed Apr 18, 2018
2 parents 0f0a353 + d74acb9 commit 431153c
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 1 deletion.
11 changes: 10 additions & 1 deletion lib/heap/client.rb
Expand Up @@ -106,7 +106,8 @@ def add_user_properties(identity, properties)
# each value must be a Number or String with fewer than 1024 characters
# @return [HeapAPI::Client] self
# @see https://heapanalytics.com/docs/server-side#track
def track(event, identity, properties = nil)
def track(event, identity, properties = nil, options = {})
options ||= {}
ensure_valid_app_id!

event_name = event.to_s
Expand All @@ -123,6 +124,14 @@ def track(event, identity, properties = nil)
ensure_valid_properties! properties
end

unless options[:timestamp].nil?
body[:timestamp] = ensure_valid_timestamp!(options[:timestamp])
end

unless options[:idempotency_key].nil?
body[:idempotency_key] = ensure_valid_idempotency_key!(options[:idempotency_key])
end

response = connection.post '/api/track', body,
'User-Agent' => user_agent
raise HeapAPI::ApiError.new(response) unless response.success?
Expand Down
48 changes: 48 additions & 0 deletions lib/heap/validations.rb
@@ -1,4 +1,5 @@
# Internal methods used to validate API input.
require 'date'

class HeapAPI::Client
# Makes sure that the client's app_id property is set.
Expand Down Expand Up @@ -43,6 +44,45 @@ def ensure_valid_identity!(identity)
end
private :ensure_valid_identity!

# Validates timestamp, making sure it's a valid iso8061 timestamp or
# number of milliseconds since epoch
#
# @param [String|Integer|DateTime|Time] timestamp
# @raise ArgumentError if timestamp is of an invalid type
# @return [String] unix epoch milliseconds or iso8061
def ensure_valid_timestamp!(timestamp)
if timestamp.kind_of?(Time)
timestamp = timestamp.to_datetime
end
if timestamp.kind_of?(DateTime)
timestamp = timestamp.strftime('%Q').to_i
end
if timestamp.kind_of?(String) && iso8601?(timestamp)
timestamp
elsif timestamp.kind_of?(Integer)
timestamp.to_s
else
raise ArgumentError,
"Unsupported timestamp format #{timestamp}. " +
"Must be iso8601 or unix epoch milliseconds."
end
end
private :ensure_valid_timestamp!

# Validate idempotency_key, making sure it's a string
#
# @param [String|Integer] idempotency_key
# @raise ArgumentError if identity is of an invalid type or too long.
# @return [String] stringified idempotency_key
def ensure_valid_idempotency_key!(idempotency_key)
unless idempotency_key.kind_of?(String) || idempotency_key.kind_of?(Integer)
raise ArgumentError, "Unsupported idempotency key format for " +
"#{idempotency_key}. Must be string or integer"
end
idempotency_key.to_s
end
private :ensure_valid_idempotency_key!

# Validates a bag of properties sent to a Heap server-side API.
#
# @param [Hash<String, String|Number>] properties key-value property bag;
Expand Down Expand Up @@ -74,4 +114,12 @@ def ensure_valid_properties!(properties)
end
end
private :ensure_valid_properties!

def iso8601?(string)
Time.iso8601(string)
true
rescue ArgumentError
false
end
private :iso8601?
end
88 changes: 88 additions & 0 deletions test/client_track_test.rb
Expand Up @@ -121,6 +121,33 @@ def test_track_with_array_property_value
exception.message
end

def test_track_with_non_date_timestamp
exception = assert_raises ArgumentError do
@heap.track 'test_track_with_array_property_value', 'test-identity', {}, :timestamp => 'foobar'
end
assert_equal ArgumentError, exception.class
assert_equal 'Unsupported timestamp format foobar. Must be iso8601 or unix epoch milliseconds.',
exception.message
end

def test_track_with_array_timestamp
exception = assert_raises ArgumentError do
@heap.track 'test_track_with_array_property_value', 'test-identity', {}, :timestamp => []
end
assert_equal ArgumentError, exception.class
assert_equal 'Unsupported timestamp format []. Must be iso8601 or unix epoch milliseconds.',
exception.message
end

def test_track_with_array_idempotency_key
exception = assert_raises ArgumentError do
@heap.track 'test_track_with_array_property_value', 'test-identity', {}, :idempotency_key => []
end
assert_equal ArgumentError, exception.class
assert_equal 'Unsupported idempotency key format for []. Must be string or integer',
exception.message
end

def test_track
@stubs.post '/api/track' do |env|
golden_body = {
Expand Down Expand Up @@ -174,6 +201,67 @@ def test_track_with_properties
'test-identity','foo' => 'bar', :heap => :hurray)
end

def test_track_with_timestamp
@stubs.post '/api/track' do |env|
golden_body = {
'app_id' => 'test-app-id',
'identity' => 'test-identity',
'event' => 'test_track_with_timestamp',
'properties' => {},
'timestamp' => '1524038400000'
}
assert_equal 'application/json', env[:request_headers]['Content-Type']
assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
assert_equal golden_body, JSON.parse(env[:body])

[200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
end

assert_equal @heap, @heap.track('test_track_with_timestamp',
'test-identity', {}, :timestamp => Time.parse("2018-04-18 08:00:00 UTC"))
end

def test_track_with_iso8601_timestamp
timestamp = "2018-04-18T22:42:38+03:00"
@stubs.post '/api/track' do |env|
golden_body = {
'app_id' => 'test-app-id',
'identity' => 'test-identity',
'event' => 'test_track_with_iso8601_timestamp',
'properties' => {},
'timestamp' => timestamp
}
assert_equal 'application/json', env[:request_headers]['Content-Type']
assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
assert_equal golden_body, JSON.parse(env[:body])

[200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
end

assert_equal @heap, @heap.track('test_track_with_iso8601_timestamp',
'test-identity', {}, :timestamp => timestamp)
end

def test_track_with_idempotency_key
@stubs.post '/api/track' do |env|
golden_body = {
'app_id' => 'test-app-id',
'identity' => 'test-identity',
'event' => 'test_track_with_idempotency_key',
'properties' => {},
'idempotency_key' => 'foobar35214532512'
}
assert_equal 'application/json', env[:request_headers]['Content-Type']
assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
assert_equal golden_body, JSON.parse(env[:body])

[200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
end

assert_equal @heap, @heap.track('test_track_with_idempotency_key',
'test-identity', {}, :idempotency_key => 'foobar35214532512')
end

def test_track_error
@stubs.post '/api/track' do |env|
[400, { 'Content-Type' => 'text/plain; encoding=utf8' }, 'Bad request']
Expand Down

0 comments on commit 431153c

Please sign in to comment.