heap 0.3.0 → 1.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.
- checksums.yaml +4 -4
- data/.travis.yml +16 -0
- data/Gemfile +11 -8
- data/Gemfile187 +23 -0
- data/LICENSE.txt +2 -2
- data/README.md +120 -0
- data/Rakefile +8 -22
- data/VERSION +1 -1
- data/heap.gemspec +42 -43
- data/lib/heap.rb +10 -23
- data/lib/heap/client.rb +192 -0
- data/lib/heap/errors.rb +18 -0
- data/lib/heap/validations.rb +57 -0
- data/test/client_add_user_properties_test.rb +124 -0
- data/test/client_test.rb +78 -0
- data/test/client_track_test.rb +151 -0
- data/test/heap_test.rb +23 -0
- data/test/helper.rb +4 -17
- metadata +51 -52
- data/CHANGELOG.mdown +0 -18
- data/Gemfile.lock +0 -91
- data/README.mdown +0 -99
- data/lib/generators/heap/install_generator.rb +0 -12
- data/lib/generators/templates/heap.rb +0 -2
- data/lib/heap/events.rb +0 -22
- data/lib/heap/railtie.rb +0 -9
- data/lib/heap/view_helpers.rb +0 -28
- data/test/test_heap.rb +0 -35
- data/test/test_view_helper.rb +0 -47
data/lib/heap/errors.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Superclass for all Heap-related errors.
|
2
|
+
class HeapAPI::Error < RuntimeError
|
3
|
+
end
|
4
|
+
|
5
|
+
# Raised when the Heap API server returns an error response.
|
6
|
+
class HeapAPI::ApiError < HeapAPI::Error
|
7
|
+
# @param [Faraday::Response] response the error response returned by the Heap
|
8
|
+
# analytics server
|
9
|
+
def initialize(response)
|
10
|
+
@response = response
|
11
|
+
|
12
|
+
super "Heap API server error: #{response.status} #{response.body}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Faraday::Response] response the error response returned by the
|
16
|
+
# Heap analytics server
|
17
|
+
attr_reader :response
|
18
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Internal methods used to validate API input.
|
2
|
+
|
3
|
+
class HeapAPI::Client
|
4
|
+
# Makes sure that the client's app_id property is set.
|
5
|
+
#
|
6
|
+
# @raise RuntimeError if the client's app_id is nil
|
7
|
+
# @return [HeapAPI::Client] self
|
8
|
+
def ensure_valid_app_id!
|
9
|
+
raise RuntimeError, 'Heap app_id not set' unless @app_id
|
10
|
+
self
|
11
|
+
end
|
12
|
+
private :ensure_valid_app_id!
|
13
|
+
|
14
|
+
# Validates a Heap server-side API event name.
|
15
|
+
#
|
16
|
+
# @param [String] event the name of the server-side event
|
17
|
+
# @raise ArgumentError if the event name is invalid
|
18
|
+
# @return [HeapAPI::Client] self
|
19
|
+
def ensure_valid_event_name!(event)
|
20
|
+
raise ArgumentError, 'Missing or empty event name' if event.empty?
|
21
|
+
raise ArgumentError, 'Event name too long' if event.length > 1024
|
22
|
+
self
|
23
|
+
end
|
24
|
+
private :ensure_valid_event_name!
|
25
|
+
|
26
|
+
# Validates a bag of properties sent to a Heap server-side API.
|
27
|
+
#
|
28
|
+
# @param [Hash<String, String|Number>] properties key-value property bag;
|
29
|
+
# each key must have fewer than 1024 characters; each value must be a
|
30
|
+
# Number or String with fewer than 1024 characters
|
31
|
+
# @raise ArgumentError if the property bag is invalid
|
32
|
+
# @return [HeapAPI::Client] self
|
33
|
+
def ensure_valid_properties!(properties)
|
34
|
+
unless properties.respond_to?(:each)
|
35
|
+
raise ArgumentError, 'Properties object does not implement #each'
|
36
|
+
end
|
37
|
+
|
38
|
+
properties.each do |key, value|
|
39
|
+
if key.to_s.length > 1024
|
40
|
+
raise ArgumentError, "Property name #{key} too long; " +
|
41
|
+
"#{key.to_s.length} is above the 1024-character limit"
|
42
|
+
end
|
43
|
+
if value.kind_of? Numeric
|
44
|
+
# TODO(pwnall): Check numerical limits, if necessary.
|
45
|
+
elsif value.kind_of?(String) || value.kind_of?(Symbol)
|
46
|
+
if value.to_s.length > 1024
|
47
|
+
raise ArgumentError, "Property #{key} value #{value.inspect} too " +
|
48
|
+
"long; #{value.to_s.length} is above the 1024-character limit"
|
49
|
+
end
|
50
|
+
else
|
51
|
+
raise ArgumentError,
|
52
|
+
"Unsupported type for property #{key} value #{value.inspect}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
private :ensure_valid_properties!
|
57
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ClientAddUserPropertiesTest < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
@stubs = Faraday::Adapter::Test::Stubs.new
|
6
|
+
@heap = HeapAPI::Client.new
|
7
|
+
@heap.app_id = 'test-app-id'
|
8
|
+
@heap.faraday_adapter = :test
|
9
|
+
@heap.faraday_adapter_args = [@stubs]
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
@stubs.verify_stubbed_calls
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_add_user_properties_without_app_id
|
17
|
+
@heap.app_id = nil
|
18
|
+
exception = assert_raises RuntimeError do
|
19
|
+
@heap.add_user_properties 'test-handle', 'key' => 'value'
|
20
|
+
end
|
21
|
+
assert_equal RuntimeError, exception.class
|
22
|
+
assert_equal 'Heap app_id not set', exception.message
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_add_user_properties_with_long_property_name
|
26
|
+
long_name = 'A' * 1025
|
27
|
+
|
28
|
+
exception = assert_raises ArgumentError do
|
29
|
+
@heap.add_user_properties 'test-handle', long_name => 'value'
|
30
|
+
end
|
31
|
+
assert_equal ArgumentError, exception.class
|
32
|
+
assert_equal "Property name #{long_name} too long; " +
|
33
|
+
'1025 is above the 1024-character limit', exception.message
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_add_user_properties_with_long_string_property_value
|
37
|
+
long_value = 'A' * 1025
|
38
|
+
exception = assert_raises ArgumentError do
|
39
|
+
@heap.add_user_properties 'test-handle', 'long_value_name' => long_value
|
40
|
+
end
|
41
|
+
assert_equal ArgumentError, exception.class
|
42
|
+
assert_equal "Property long_value_name value \"#{long_value}\" too " +
|
43
|
+
'long; 1025 is above the 1024-character limit', exception.message
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_add_user_properties_with_long_symbol_property_value
|
47
|
+
long_value = ('A' * 1025).to_sym
|
48
|
+
exception = assert_raises ArgumentError do
|
49
|
+
@heap.add_user_properties 'test-handle', 'long_value_name' => long_value
|
50
|
+
end
|
51
|
+
assert_equal ArgumentError, exception.class
|
52
|
+
assert_equal "Property long_value_name value :#{long_value} too long; " +
|
53
|
+
'1025 is above the 1024-character limit', exception.message
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_add_user_properties_with_array_property_value
|
57
|
+
exception = assert_raises ArgumentError do
|
58
|
+
@heap.add_user_properties 'test-handle', 'array_value_name' => []
|
59
|
+
end
|
60
|
+
assert_equal ArgumentError, exception.class
|
61
|
+
assert_equal 'Unsupported type for property array_value_name value []',
|
62
|
+
exception.message
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def test_add_user_properties
|
67
|
+
@stubs.post '/api/identify' do |env|
|
68
|
+
golden_body = {
|
69
|
+
'app_id' => 'test-app-id',
|
70
|
+
'identity' => 'test-handle',
|
71
|
+
'properties' => { 'foo' => 'bar', 'heap' => 'hurray' }
|
72
|
+
}
|
73
|
+
assert_equal 'application/json', env[:request_headers]['Content-Type']
|
74
|
+
assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
|
75
|
+
assert_equal golden_body, JSON.parse(env[:body])
|
76
|
+
|
77
|
+
[200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
|
78
|
+
end
|
79
|
+
|
80
|
+
assert_equal @heap, @heap.add_user_properties('test-handle',
|
81
|
+
'foo' => 'bar', :heap => :hurray)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_add_user_properties_error
|
85
|
+
@stubs.post '/api/identify' do |env|
|
86
|
+
[400, { 'Content-Type' => 'text/plain; encoding=utf8' }, 'Bad request']
|
87
|
+
end
|
88
|
+
|
89
|
+
exception = assert_raises HeapAPI::Error do
|
90
|
+
@heap.add_user_properties 'test-handle', 'foo' => 'bar'
|
91
|
+
end
|
92
|
+
|
93
|
+
assert_equal HeapAPI::ApiError, exception.class
|
94
|
+
assert_equal 'Heap API server error: 400 Bad request', exception.message
|
95
|
+
assert_kind_of Faraday::Response, exception.response
|
96
|
+
assert_equal 400, exception.response.status
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_add_user_properties_integration
|
100
|
+
@heap.app_id = '3000610572'
|
101
|
+
@heap.faraday_adapter = :net_http
|
102
|
+
@heap.faraday_adapter_args = []
|
103
|
+
|
104
|
+
assert_equal @heap, @heap.add_user_properties('test-handle',
|
105
|
+
'language/ruby' => 1, 'heap/heap-ruby' => 1)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_add_user_properties_error_integration
|
109
|
+
@heap.faraday_adapter = :net_http
|
110
|
+
@heap.faraday_adapter_args = []
|
111
|
+
|
112
|
+
assert_raises HeapAPI::Error do
|
113
|
+
@heap.add_user_properties 'test-handle',
|
114
|
+
'language/ruby' => 1, 'heap/heap-ruby' => 1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_add_user_properties_with_stubbed_connection
|
119
|
+
@heap.stubbed = true
|
120
|
+
|
121
|
+
assert_equal @heap, @heap.add_user_properties('test-handle',
|
122
|
+
'foo' => 'bar', :heap => :hurray)
|
123
|
+
end
|
124
|
+
end
|
data/test/client_test.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ClientTest < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
@heap = HeapAPI::Client.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_default_app_id
|
9
|
+
assert_equal nil, @heap.app_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_default_stubbed
|
13
|
+
assert_equal false, @heap.stubbed
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_default_faraday_adapter
|
17
|
+
assert_equal Faraday.default_adapter, @heap.faraday_adapter
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_default_faraday_adapter_args
|
21
|
+
assert_equal([], @heap.faraday_adapter_args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_faraday_adapter_args_setter
|
25
|
+
stubs = Faraday::Adapter::Test::Stubs.new
|
26
|
+
@heap.faraday_adapter_args = [stubs]
|
27
|
+
assert_equal([stubs], @heap.faraday_adapter_args)
|
28
|
+
|
29
|
+
@heap.faraday_adapter = :test
|
30
|
+
@heap.connection # Materialize the Faraday connection.
|
31
|
+
exception = assert_raises RuntimeError do
|
32
|
+
@heap.faraday_adapter_args = [:derp]
|
33
|
+
end
|
34
|
+
assert_equal RuntimeError, exception.class
|
35
|
+
assert_equal 'Faraday connection already initialized', exception.message
|
36
|
+
assert_equal [stubs], @heap.faraday_adapter_args
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_stubbed_setter
|
40
|
+
@heap.app_id = 'test-app-id'
|
41
|
+
@heap.faraday_adapter = :net_http
|
42
|
+
|
43
|
+
@heap.stubbed = true
|
44
|
+
assert_equal @heap, @heap.track('test_stubbed_setter', 'test-handle',
|
45
|
+
'language' => 'ruby', 'project' => 'heap/heap-ruby')
|
46
|
+
stubbed_connection = @heap.connection
|
47
|
+
|
48
|
+
@heap.stubbed = false
|
49
|
+
assert_raises HeapAPI::Error do
|
50
|
+
@heap.track 'test_stubbed_setter', 'test-handle',
|
51
|
+
'language' => 'ruby', 'project' => 'heap/heap-ruby'
|
52
|
+
end
|
53
|
+
live_connection = @heap.connection
|
54
|
+
|
55
|
+
@heap.stubbed = true
|
56
|
+
assert_equal stubbed_connection, @heap.connection
|
57
|
+
|
58
|
+
@heap.stubbed = false
|
59
|
+
assert_equal live_connection, @heap.connection
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_default_user_agent
|
63
|
+
assert_match(/^heap-ruby\/[0-9.]+ /, @heap.user_agent)
|
64
|
+
assert_match(/ faraday\/[0-9.]+ /, @heap.user_agent)
|
65
|
+
assert_match(/ ruby\/[0-9.]+ \(.+\)$/, @heap.user_agent)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_user_agent_setter
|
69
|
+
@heap.user_agent = 'sparky/4.2'
|
70
|
+
assert_equal 'sparky/4.2', @heap.user_agent
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_constructor_options
|
74
|
+
heap = Heap.new :app_id => 'test-app-id', :stubbed => true
|
75
|
+
assert_equal 'test-app-id', heap.app_id
|
76
|
+
assert_equal true, heap.stubbed
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ClientTrackTest < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
@stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
6
|
+
end
|
7
|
+
@heap = HeapAPI::Client.new
|
8
|
+
@heap.app_id = 'test-app-id'
|
9
|
+
@heap.faraday_adapter = :test
|
10
|
+
@heap.faraday_adapter_args = [@stubs]
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@stubs.verify_stubbed_calls
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_track_without_app_id
|
18
|
+
@heap.app_id = nil
|
19
|
+
exception = assert_raises RuntimeError do
|
20
|
+
@heap.track 'test_track_without_app_id', 'test-handle'
|
21
|
+
end
|
22
|
+
assert_equal RuntimeError, exception.class
|
23
|
+
assert_equal 'Heap app_id not set', exception.message
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_track_without_event_name
|
27
|
+
exception = assert_raises ArgumentError do
|
28
|
+
@heap.track '', 'test-handle'
|
29
|
+
end
|
30
|
+
assert_equal ArgumentError, exception.class
|
31
|
+
assert_equal 'Missing or empty event name', exception.message
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_track_with_long_event_name
|
35
|
+
exception = assert_raises ArgumentError do
|
36
|
+
@heap.track 'A' * 1025, 'test-handle'
|
37
|
+
end
|
38
|
+
assert_equal ArgumentError, exception.class
|
39
|
+
assert_equal 'Event name too long', exception.message
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_track_with_long_property_name
|
43
|
+
long_name = 'A' * 1025
|
44
|
+
exception = assert_raises ArgumentError do
|
45
|
+
@heap.track 'test_track_with_long_property_name', 'test-handle',
|
46
|
+
long_name => 'value'
|
47
|
+
end
|
48
|
+
assert_equal ArgumentError, exception.class
|
49
|
+
assert_equal "Property name #{long_name} too long; " +
|
50
|
+
"1025 is above the 1024-character limit", exception.message
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_track_with_long_string_property_value
|
54
|
+
long_value = 'A' * 1025
|
55
|
+
exception = assert_raises ArgumentError do
|
56
|
+
@heap.track 'test_track_with_long_string_property_value', 'test-handle',
|
57
|
+
'long_value_name' => long_value
|
58
|
+
end
|
59
|
+
assert_equal ArgumentError, exception.class
|
60
|
+
assert_equal "Property long_value_name value \"#{long_value}\" too " +
|
61
|
+
'long; 1025 is above the 1024-character limit', exception.message
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_track_with_long_symbol_property_value
|
65
|
+
long_value = ('A' * 1025).to_sym
|
66
|
+
exception = assert_raises ArgumentError do
|
67
|
+
@heap.track 'test_track_with_long_symbol_property_value', 'test-handle',
|
68
|
+
'long_value_name' => long_value
|
69
|
+
end
|
70
|
+
assert_equal ArgumentError, exception.class
|
71
|
+
assert_equal "Property long_value_name value :#{long_value} too long; " +
|
72
|
+
'1025 is above the 1024-character limit', exception.message
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_track
|
76
|
+
@stubs.post '/api/track' do |env|
|
77
|
+
golden_body = {
|
78
|
+
'app_id' => 'test-app-id',
|
79
|
+
'identity' => 'test-handle',
|
80
|
+
'event' => 'test_track',
|
81
|
+
}
|
82
|
+
assert_equal 'application/json', env[:request_headers]['Content-Type']
|
83
|
+
assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
|
84
|
+
assert_equal golden_body, JSON.parse(env[:body])
|
85
|
+
|
86
|
+
[200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
|
87
|
+
end
|
88
|
+
|
89
|
+
assert_equal @heap, @heap.track('test_track', 'test-handle')
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_track_with_properties
|
93
|
+
@stubs.post '/api/track' do |env|
|
94
|
+
golden_body = {
|
95
|
+
'app_id' => 'test-app-id',
|
96
|
+
'identity' => 'test-handle',
|
97
|
+
'event' => 'test_track_with_properties',
|
98
|
+
'properties' => { 'foo' => 'bar', 'heap' => 'hurray' }
|
99
|
+
}
|
100
|
+
assert_equal 'application/json', env[:request_headers]['Content-Type']
|
101
|
+
assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
|
102
|
+
assert_equal golden_body, JSON.parse(env[:body])
|
103
|
+
|
104
|
+
[200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
|
105
|
+
end
|
106
|
+
|
107
|
+
assert_equal @heap, @heap.track('test_track_with_properties',
|
108
|
+
'test-handle','foo' => 'bar', :heap => :hurray)
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_track_error
|
112
|
+
@stubs.post '/api/track' do |env|
|
113
|
+
[400, { 'Content-Type' => 'text/plain; encoding=utf8' }, 'Bad request']
|
114
|
+
end
|
115
|
+
|
116
|
+
exception = assert_raises HeapAPI::Error do
|
117
|
+
@heap.track('test-handle', 'test_track')
|
118
|
+
end
|
119
|
+
assert_equal HeapAPI::ApiError, exception.class
|
120
|
+
assert_equal 'Heap API server error: 400 Bad request', exception.message
|
121
|
+
assert_kind_of Faraday::Response, exception.response
|
122
|
+
assert_equal 400, exception.response.status
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_track_integration
|
126
|
+
@heap.app_id = '3000610572'
|
127
|
+
@heap.faraday_adapter = :net_http
|
128
|
+
@heap.faraday_adapter_args = []
|
129
|
+
|
130
|
+
assert_equal @heap, @heap.track('test_track_integration', 'test-handle',
|
131
|
+
'language' => 'ruby', 'project' => 'heap/heap-ruby')
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_track_error_integration
|
135
|
+
@heap.faraday_adapter = :net_http
|
136
|
+
@heap.faraday_adapter_args = []
|
137
|
+
|
138
|
+
assert_raises HeapAPI::Error do
|
139
|
+
@heap.track('test_track_integration', 'test-handle',
|
140
|
+
'language' => 'ruby', 'project' => 'heap/heap-ruby')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def test_track_with_stubbed_connection
|
146
|
+
@heap.stubbed = true
|
147
|
+
|
148
|
+
assert_equal @heap, @heap.add_user_properties('test-handle',
|
149
|
+
'foo' => 'bar', :heap => :hurray)
|
150
|
+
end
|
151
|
+
end
|
data/test/heap_test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HeapTest < MiniTest::Test
|
4
|
+
def test_heap_app_id
|
5
|
+
assert_equal nil, Heap.app_id
|
6
|
+
begin
|
7
|
+
Heap.app_id = 'global-app-id'
|
8
|
+
assert_equal 'global-app-id', Heap.app_id
|
9
|
+
ensure
|
10
|
+
Heap.app_id = nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_heap_stubbed
|
15
|
+
assert_equal false, Heap.stubbed
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_heap_new
|
19
|
+
client = Heap.new :app_id => 'local-app-id', :stubbed => true
|
20
|
+
assert_equal 'local-app-id', client.app_id
|
21
|
+
assert_equal true, client.stubbed
|
22
|
+
end
|
23
|
+
end
|