amplitude-api 0.0.10 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +14 -0
- data/Changelog.md +12 -0
- data/Gemfile +4 -4
- data/Gemfile.lock +44 -41
- data/amplitude-api.gemspec +5 -6
- data/lib/amplitude_api.rb +49 -16
- data/lib/amplitude_api/config.rb +30 -0
- data/lib/amplitude_api/event.rb +61 -87
- data/lib/amplitude_api/identification.rb +1 -1
- data/lib/amplitude_api/version.rb +1 -1
- data/readme.md +18 -1
- data/spec/lib/amplitude_api/event_spec.rb +49 -3
- data/spec/lib/amplitude_api_spec.rb +120 -61
- data/spec/spec_helper.rb +2 -1
- metadata +28 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8b743d15865f804837f6f3abb9557fa35aa6a32ae360fae0edfeaab0e7905b81
|
4
|
+
data.tar.gz: 8c58bd5c4a21a36394cca7ce58da56b1e4135fc4ac574001e31350bda5cf3d69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bf4707b9fe9905e95fdeee4c66044dfce02a2f4bae7fe939cffc343957ff4b2eb3d0dcf41d9c9dc958048f14fc8039c5d4e5c375103b7aeec4260e5a5a5c2d5
|
7
|
+
data.tar.gz: 6cb4d59c569e8ccb3d8fc1f52920baba4ae6fa1da605115e6ed393a05119d1b275e58d00d18349cb840dd84b11b58416f3639e31eb95e4fb6c88eac55a378084
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,17 @@
|
|
1
1
|
require: rubocop-rspec
|
2
2
|
Metrics/LineLength:
|
3
3
|
Max: 120
|
4
|
+
|
5
|
+
# Disable some failing rspec cops that fail from upgrading gems until we can clean things up a bit.
|
6
|
+
RSpec/ExampleLength:
|
7
|
+
Enabled: false
|
8
|
+
Metrics/BlockLength:
|
9
|
+
Enabled: false
|
10
|
+
RSpec/MultipleExpectations:
|
11
|
+
Enabled: false
|
12
|
+
RSpec/NestedGroups:
|
13
|
+
Enabled: false
|
14
|
+
RSpec/MessageSpies:
|
15
|
+
Enabled: false
|
16
|
+
RSpec/ContextWording:
|
17
|
+
Enabled: false
|
data/Changelog.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Amplitude API Changelog
|
2
|
+
|
3
|
+
We would like to think our many [contributors](https://github.com/toothrot/amplitude-api/graphs/contributors) for
|
4
|
+
suggestions, ideas and improvements to Amplitude API.
|
5
|
+
|
6
|
+
## 0.0.10 (2017-09-13)
|
7
|
+
|
8
|
+
* Allow to use "Event Segmentation" via API ([#23](https://github.com/toothrot/amplitude-api/pull/23)).
|
9
|
+
|
10
|
+
## Older releases
|
11
|
+
|
12
|
+
Please see the v0.0.9 tag.
|
data/Gemfile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# Generated from /Users/alex/development/amplitude-api/amplitude-api.gemspec
|
2
2
|
source 'https://rubygems.org'
|
3
3
|
|
4
|
-
gem 'typhoeus', '~> 1.
|
4
|
+
gem 'typhoeus', '~> 1.1'
|
5
5
|
|
6
6
|
group :development, :test do
|
7
|
-
gem 'pry', '~> 0.
|
8
|
-
gem 'rake', '>=
|
7
|
+
gem 'pry', '~> 0.12.2'
|
8
|
+
gem 'rake', '>= 12.0'
|
9
9
|
gem 'rspec', '>= 2.99.0'
|
10
|
-
gem 'rubocop', '~> 0.
|
10
|
+
gem 'rubocop', '~> 0.66.0', require: false
|
11
11
|
gem 'rubocop-rspec'
|
12
12
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,58 +1,61 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
ast (2.
|
5
|
-
coderay (1.1.
|
6
|
-
diff-lcs (1.
|
7
|
-
ethon (0.
|
4
|
+
ast (2.4.0)
|
5
|
+
coderay (1.1.2)
|
6
|
+
diff-lcs (1.3)
|
7
|
+
ethon (0.12.0)
|
8
8
|
ffi (>= 1.3.0)
|
9
|
-
ffi (1.
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
rspec-
|
23
|
-
rspec-
|
24
|
-
|
25
|
-
|
26
|
-
|
9
|
+
ffi (1.10.0)
|
10
|
+
jaro_winkler (1.5.2)
|
11
|
+
method_source (0.9.2)
|
12
|
+
parallel (1.17.0)
|
13
|
+
parser (2.6.2.0)
|
14
|
+
ast (~> 2.4.0)
|
15
|
+
pry (0.12.2)
|
16
|
+
coderay (~> 1.1.0)
|
17
|
+
method_source (~> 0.9.0)
|
18
|
+
psych (3.1.0)
|
19
|
+
rainbow (3.0.0)
|
20
|
+
rake (12.3.2)
|
21
|
+
rspec (3.8.0)
|
22
|
+
rspec-core (~> 3.8.0)
|
23
|
+
rspec-expectations (~> 3.8.0)
|
24
|
+
rspec-mocks (~> 3.8.0)
|
25
|
+
rspec-core (3.8.0)
|
26
|
+
rspec-support (~> 3.8.0)
|
27
|
+
rspec-expectations (3.8.2)
|
27
28
|
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
-
rspec-support (~> 3.
|
29
|
-
rspec-mocks (3.
|
29
|
+
rspec-support (~> 3.8.0)
|
30
|
+
rspec-mocks (3.8.0)
|
30
31
|
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
-
rspec-support (~> 3.
|
32
|
-
rspec-support (3.
|
33
|
-
rubocop (0.
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
rspec-support (~> 3.8.0)
|
33
|
+
rspec-support (3.8.0)
|
34
|
+
rubocop (0.66.0)
|
35
|
+
jaro_winkler (~> 1.5.1)
|
36
|
+
parallel (~> 1.10)
|
37
|
+
parser (>= 2.5, != 2.5.1.1)
|
38
|
+
psych (>= 3.1.0)
|
39
|
+
rainbow (>= 2.2.2, < 4.0)
|
37
40
|
ruby-progressbar (~> 1.7)
|
38
|
-
unicode-display_width (
|
39
|
-
rubocop-rspec (1.
|
40
|
-
|
41
|
-
|
42
|
-
typhoeus (1.
|
41
|
+
unicode-display_width (>= 1.4.0, < 1.6)
|
42
|
+
rubocop-rspec (1.32.0)
|
43
|
+
rubocop (>= 0.60.0)
|
44
|
+
ruby-progressbar (1.10.0)
|
45
|
+
typhoeus (1.3.1)
|
43
46
|
ethon (>= 0.9.0)
|
44
|
-
unicode-display_width (
|
47
|
+
unicode-display_width (1.5.0)
|
45
48
|
|
46
49
|
PLATFORMS
|
47
50
|
ruby
|
48
51
|
|
49
52
|
DEPENDENCIES
|
50
|
-
pry (~> 0.
|
51
|
-
rake (>=
|
53
|
+
pry (~> 0.12.2)
|
54
|
+
rake (>= 12.0)
|
52
55
|
rspec (>= 2.99.0)
|
53
|
-
rubocop (~> 0.
|
56
|
+
rubocop (~> 0.66.0)
|
54
57
|
rubocop-rspec
|
55
|
-
typhoeus (~> 1.
|
58
|
+
typhoeus (~> 1.1)
|
56
59
|
|
57
60
|
BUNDLED WITH
|
58
|
-
1.
|
61
|
+
1.17.2
|
data/amplitude-api.gemspec
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'amplitude_api/version'
|
5
4
|
|
@@ -18,9 +17,9 @@ Gem::Specification.new do |spec|
|
|
18
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
18
|
spec.require_paths = ['lib']
|
20
19
|
|
20
|
+
spec.add_development_dependency 'pry', '~> 0.12.2'
|
21
|
+
spec.add_development_dependency 'rake', '~> 12.0', '>= 12.0'
|
21
22
|
spec.add_development_dependency 'rspec', '~> 2.99', '>= 2.99.0'
|
22
|
-
spec.
|
23
|
-
spec.
|
24
|
-
spec.add_dependency 'typhoeus', '~> 1.0.2'
|
25
|
-
spec.required_ruby_version = '~> 2.0'
|
23
|
+
spec.add_dependency 'typhoeus', '~> 1.0'
|
24
|
+
spec.required_ruby_version = '~> 2.2'
|
26
25
|
end
|
data/lib/amplitude_api.rb
CHANGED
@@ -1,25 +1,35 @@
|
|
1
1
|
require 'json'
|
2
|
-
require 'bundler/setup'
|
3
2
|
require 'typhoeus'
|
4
|
-
require_relative 'amplitude_api/event'
|
5
|
-
require_relative 'amplitude_api/identification'
|
6
3
|
|
7
4
|
# AmplitudeAPI
|
8
5
|
class AmplitudeAPI
|
6
|
+
require_relative 'amplitude_api/config'
|
7
|
+
require_relative 'amplitude_api/event'
|
8
|
+
require_relative 'amplitude_api/identification'
|
9
|
+
|
9
10
|
TRACK_URI_STRING = 'https://api.amplitude.com/httpapi'.freeze
|
10
11
|
IDENTIFY_URI_STRING = 'https://api.amplitude.com/identify'.freeze
|
11
12
|
SEGMENTATION_URI_STRING = 'https://amplitude.com/api/2/events/segmentation'.freeze
|
13
|
+
DELETION_URI_STRING = 'https://amplitude.com/api/2/deletions/users'.freeze
|
12
14
|
|
13
15
|
USER_WITH_NO_ACCOUNT = "user who doesn't have an account".freeze
|
14
16
|
|
15
17
|
class << self
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
def config
|
19
|
+
Config.instance
|
20
|
+
end
|
21
|
+
|
22
|
+
def configure
|
23
|
+
yield config
|
24
|
+
end
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
|
26
|
+
def api_key
|
27
|
+
config.api_key
|
28
|
+
end
|
29
|
+
|
30
|
+
def secret_key
|
31
|
+
config.secret_key
|
32
|
+
end
|
23
33
|
|
24
34
|
# ==== Event Tracking related methods
|
25
35
|
|
@@ -141,17 +151,40 @@ class AmplitudeAPI
|
|
141
151
|
# returned (default: 100). The maximum limit is 1000.
|
142
152
|
#
|
143
153
|
# @return [ Typhoeus::Response ]
|
144
|
-
def segmentation(
|
154
|
+
def segmentation(event, start_time, end_time, **options)
|
145
155
|
Typhoeus.get SEGMENTATION_URI_STRING, userpwd: "#{api_key}:#{secret_key}", params: {
|
146
|
-
e:
|
147
|
-
m:
|
156
|
+
e: event.to_json,
|
157
|
+
m: options[:m],
|
148
158
|
start: start_time.strftime('%Y%m%d'),
|
149
|
-
end:
|
150
|
-
i:
|
151
|
-
s:
|
152
|
-
g:
|
159
|
+
end: end_time.strftime('%Y%m%d'),
|
160
|
+
i: options[:i],
|
161
|
+
s: (options[:s] || []).map(&:to_json),
|
162
|
+
g: options[:g],
|
153
163
|
limit: options[:limit]
|
154
164
|
}.delete_if { |_, value| value.nil? }
|
155
165
|
end
|
166
|
+
|
167
|
+
# ==== GDPR compliance methods
|
168
|
+
|
169
|
+
# Delete a user from amplitude when they request it to comply with GDPR
|
170
|
+
# You must pass in either an array of user_ids or an array of amplitude_ids
|
171
|
+
#
|
172
|
+
# @param [ user_ids ] (optional) the user_ids to delete
|
173
|
+
# based on your database
|
174
|
+
# @param [ amplitude_ids ] (optional) the amplitude_ids to delete
|
175
|
+
# based on the amplitude database
|
176
|
+
# @param [ requester ] the email address of the person who
|
177
|
+
# is requesting the deletion, optional but useful for reporting
|
178
|
+
#
|
179
|
+
# @return [ Typhoeus::Response ]
|
180
|
+
def delete(user_ids: nil, amplitude_ids: nil, requester: nil)
|
181
|
+
Typhoeus.post DELETION_URI_STRING,
|
182
|
+
userpwd: "#{api_key}:#{config.secret_key}",
|
183
|
+
body: {
|
184
|
+
amplitude_ids: amplitude_ids,
|
185
|
+
user_ids: user_ids,
|
186
|
+
requester: requester
|
187
|
+
}.delete_if { |_, value| value.nil? }
|
188
|
+
end
|
156
189
|
end
|
157
190
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
class AmplitudeAPI
|
4
|
+
# AmplitudeAPI::Config
|
5
|
+
class Config
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_accessor :api_key, :secret_key, :whitelist, :time_formatter,
|
9
|
+
:event_properties_formatter, :user_properties_formatter
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
self.class.defaults.each { |k, v| send("#{k}=", v) }
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def defaults
|
17
|
+
{
|
18
|
+
api_key: nil,
|
19
|
+
secret_key: nil,
|
20
|
+
whitelist: %i[user_id device_id event_type time
|
21
|
+
event_properties user_properties time ip platform country insert_id
|
22
|
+
revenue_type price quantity product_id],
|
23
|
+
time_formatter: ->(time) { time ? time.to_i * 1_000 : nil },
|
24
|
+
event_properties_formatter: ->(props) { props || {} },
|
25
|
+
user_properties_formatter: ->(props) { props || {} }
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/amplitude_api/event.rb
CHANGED
@@ -1,71 +1,19 @@
|
|
1
1
|
class AmplitudeAPI
|
2
2
|
# AmplitudeAPI::Event
|
3
3
|
class Event
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# @!attribute [ rw ] device_id
|
8
|
-
# @return [ String ] the device_id to be sent to Amplitude
|
9
|
-
attr_accessor :device_id
|
10
|
-
# @!attribute [ rw ] event_type
|
11
|
-
# @return [ String ] the event_type to be sent to Amplitude
|
12
|
-
attr_accessor :event_type
|
13
|
-
# @!attribute [ rw ] event_properties
|
14
|
-
# @return [ String ] the event_properties to be attached to the Amplitude Event
|
15
|
-
attr_accessor :event_properties
|
16
|
-
# @!attribute [ rw ] user_properties
|
17
|
-
# @return [ String ] the user_properties to be passed for the user
|
18
|
-
attr_accessor :user_properties
|
19
|
-
# @!attribute [ rw ] time
|
20
|
-
# @return [ Time ] Time that the event occurred (defaults to now)
|
21
|
-
attr_accessor :time
|
22
|
-
# @!attribute [ rw ] ip
|
23
|
-
# @return [ String ] IP address of the user
|
24
|
-
attr_accessor :ip
|
25
|
-
|
26
|
-
# @!attribute [ rw ] insert_id
|
27
|
-
# @return [ String ] the unique identifier to be sent to Amplitude
|
28
|
-
attr_accessor :insert_id
|
29
|
-
|
30
|
-
# @!attribute [ rw ] price
|
31
|
-
# @return [ String ] (required for revenue data) price of the item purchased
|
32
|
-
attr_accessor :price
|
33
|
-
|
34
|
-
# @!attribute [ rw ] quantity
|
35
|
-
# @return [ String ] (required for revenue data, defaults to 1 if not specified) quantity of the item purchased
|
36
|
-
attr_accessor :quantity
|
37
|
-
|
38
|
-
# @!attribute [ rw ] product_id
|
39
|
-
# @return [ String ] an identifier for the product. (Note: you must send a price and quantity with this field)
|
40
|
-
attr_accessor :product_id
|
41
|
-
|
42
|
-
# @!attribute [ rw ] revenue_type
|
43
|
-
# @return [ String ] type of revenue. (Note: you must send a price and quantity with this field)
|
44
|
-
attr_accessor :revenue_type
|
4
|
+
AmplitudeAPI::Config.instance.whitelist.each do |attribute|
|
5
|
+
instance_eval("attr_accessor :#{attribute}", __FILE__, __LINE__)
|
6
|
+
end
|
45
7
|
|
46
8
|
# Create a new Event
|
47
9
|
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# @param [ String ] event_type a name for the event
|
51
|
-
# @param [ Hash ] event_properties various properties to attach to the event
|
52
|
-
# @param [ Time ] Time that the event occurred (defaults to now)
|
53
|
-
# @param [ Double ] price (optional, but required for revenue data) price of the item purchased
|
54
|
-
# @param [ Integer ] quantity (optional, but required for revenue data) quantity of the item purchased
|
55
|
-
# @param [ String ] product_id (optional) an identifier for the product.
|
56
|
-
# @param [ String ] revenue_type (optional) type of revenue
|
57
|
-
# @param [ String ] IP address of the user
|
58
|
-
# @param [ String ] insert_id a unique identifier for the event
|
10
|
+
# See (Amplitude HTTP API Documentation)[https://amplitude.zendesk.com/hc/en-us/articles/204771828-HTTP-API]
|
11
|
+
# for a list of valid parameters and their types.
|
59
12
|
def initialize(attributes = {})
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
self.user_properties = getopt(attributes, :user_properties, {})
|
65
|
-
self.time = getopt(attributes, :time)
|
66
|
-
self.ip = getopt(attributes, :ip, '')
|
67
|
-
self.insert_id = getopt(attributes, :insert_id)
|
68
|
-
validate_revenue_arguments(attributes)
|
13
|
+
attributes.each do |k, v|
|
14
|
+
send("#{k}=", v) if respond_to?("#{k}=")
|
15
|
+
end
|
16
|
+
validate_arguments
|
69
17
|
end
|
70
18
|
|
71
19
|
def user_id=(value)
|
@@ -81,47 +29,73 @@ class AmplitudeAPI
|
|
81
29
|
#
|
82
30
|
# Used for serialization and comparison
|
83
31
|
def to_hash
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
32
|
+
event = {
|
33
|
+
event_type: event_type,
|
34
|
+
event_properties: formatted_event_properties,
|
35
|
+
user_properties: formatted_user_properties
|
36
|
+
}
|
37
|
+
event[:user_id] = user_id if user_id
|
38
|
+
event[:device_id] = device_id if device_id
|
39
|
+
event.merge(optional_properties).merge(revenue_hash)
|
40
|
+
end
|
41
|
+
alias to_h to_hash
|
42
|
+
|
43
|
+
# @return [ Hash ] Optional properties
|
44
|
+
def optional_properties
|
45
|
+
%i[device_id time ip platform country insert_id].map do |prop|
|
46
|
+
val = prop == :time ? formatted_time : send(prop)
|
47
|
+
val ? [prop, val] : nil
|
48
|
+
end.compact.to_h
|
91
49
|
end
|
92
50
|
|
93
|
-
# @return [
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
51
|
+
# @return [ true, false ]
|
52
|
+
#
|
53
|
+
# Returns true if the event type matches one reserved by Amplitude API.
|
54
|
+
def reserved_event?(type)
|
55
|
+
['[Amplitude] Start Session',
|
56
|
+
'[Amplitude] End Session',
|
57
|
+
'[Amplitude] Revenue',
|
58
|
+
'[Amplitude] Revenue (Verified)',
|
59
|
+
'[Amplitude] Revenue (Unverified)',
|
60
|
+
'[Amplitude] Merged User'].include?(type)
|
100
61
|
end
|
101
62
|
|
102
63
|
# @return [ true, false ]
|
103
64
|
#
|
104
65
|
# Compares +to_hash+ for equality
|
105
66
|
def ==(other)
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
false
|
110
|
-
end
|
67
|
+
return false unless other.respond_to?(:to_h)
|
68
|
+
|
69
|
+
to_h == other.to_h
|
111
70
|
end
|
112
71
|
|
113
72
|
private
|
114
73
|
|
115
74
|
def formatted_time
|
116
|
-
time
|
75
|
+
Config.instance.time_formatter.call(time)
|
76
|
+
end
|
77
|
+
|
78
|
+
def formatted_event_properties
|
79
|
+
Config.instance.event_properties_formatter.call(event_properties)
|
80
|
+
end
|
81
|
+
|
82
|
+
def formatted_user_properties
|
83
|
+
Config.instance.user_properties_formatter.call(user_properties)
|
84
|
+
end
|
85
|
+
|
86
|
+
def validate_arguments
|
87
|
+
validate_required_arguments
|
88
|
+
validate_revenue_arguments
|
89
|
+
end
|
90
|
+
|
91
|
+
def validate_required_arguments
|
92
|
+
raise ArgumentError, 'You must provide user_id or device_id (or both)' unless user_id || device_id
|
93
|
+
raise ArgumentError, 'You must provide event_type' unless event_type
|
94
|
+
raise ArgumentError, 'Invalid event_type - cannot match a reserved event name' if reserved_event?(event_type)
|
117
95
|
end
|
118
96
|
|
119
|
-
def validate_revenue_arguments
|
120
|
-
self.
|
121
|
-
self.quantity = getopt(options, :quantity, 1) if price
|
122
|
-
self.product_id = getopt(options, :product_id)
|
123
|
-
self.revenue_type = getopt(options, :revenue_type)
|
124
|
-
return if price
|
97
|
+
def validate_revenue_arguments
|
98
|
+
return self.quantity ||= 1 if price
|
125
99
|
raise ArgumentError, 'You must provide a price in order to use the product_id' if product_id
|
126
100
|
raise ArgumentError, 'You must provide a price in order to use the revenue_type' if revenue_type
|
127
101
|
end
|
@@ -3,7 +3,7 @@ class AmplitudeAPI
|
|
3
3
|
class Identification
|
4
4
|
# @!attribute [ rw ] user_id
|
5
5
|
# @return [ String ] the user_id to be sent to Amplitude
|
6
|
-
|
6
|
+
attr_reader :user_id
|
7
7
|
# @!attribute [ rw ] device_id
|
8
8
|
# @return [ String ] the device_id to be sent to Amplitude
|
9
9
|
attr_accessor :device_id
|
data/readme.md
CHANGED
@@ -15,7 +15,8 @@ The following code snippet will immediately track an event to the Amplitude API.
|
|
15
15
|
|
16
16
|
```ruby
|
17
17
|
# Configure your Amplitude API key
|
18
|
-
AmplitudeAPI.api_key = "abcdef123456"
|
18
|
+
AmplitudeAPI.config.api_key = "abcdef123456"
|
19
|
+
|
19
20
|
|
20
21
|
event = AmplitudeAPI::Event.new({
|
21
22
|
user_id: "123",
|
@@ -30,6 +31,22 @@ event = AmplitudeAPI::Event.new({
|
|
30
31
|
AmplitudeAPI.track(event)
|
31
32
|
```
|
32
33
|
|
34
|
+
## GDPR Compliance
|
35
|
+
|
36
|
+
The following code snippet will delete a user from amplitude
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
# Configure your Amplitude API key
|
40
|
+
AmplitudeAPI.config.api_key = "abcdef123456"
|
41
|
+
|
42
|
+
# Configure your Amplitude Secret Key
|
43
|
+
AmplitudeAPI.config.secret_key = "secretMcSecret"
|
44
|
+
|
45
|
+
AmplitudeAPI.delete(user_ids: [233],
|
46
|
+
requester: "privacy@mycompany.com"
|
47
|
+
)
|
48
|
+
```
|
49
|
+
|
33
50
|
Currently, we are using this in Rails and using ActiveJob to dispatch events asynchronously. I plan on moving background/asynchronous support into this gem.
|
34
51
|
|
35
52
|
## What's Next
|
@@ -42,7 +42,7 @@ describe AmplitudeAPI::Event do
|
|
42
42
|
describe 'init' do
|
43
43
|
context 'attributes' do
|
44
44
|
it 'accepts string attributes' do
|
45
|
-
time = Time.
|
45
|
+
time = Time.at(1_451_606_400_000 / 1_000)
|
46
46
|
event = described_class.new(
|
47
47
|
'user_id' => 123,
|
48
48
|
'device_id' => 'abcd',
|
@@ -51,6 +51,8 @@ describe AmplitudeAPI::Event do
|
|
51
51
|
'user_properties' => { 'c' => 'd' },
|
52
52
|
'time' => time,
|
53
53
|
'ip' => '127.0.0.1',
|
54
|
+
'platform' => 'Web',
|
55
|
+
'country' => 'United States',
|
54
56
|
'insert_id' => 'bestId'
|
55
57
|
)
|
56
58
|
|
@@ -61,11 +63,13 @@ describe AmplitudeAPI::Event do
|
|
61
63
|
user_properties: { 'c' => 'd' },
|
62
64
|
time: 1_451_606_400_000,
|
63
65
|
ip: '127.0.0.1',
|
66
|
+
platform: 'Web',
|
67
|
+
country: 'United States',
|
64
68
|
insert_id: 'bestId')
|
65
69
|
end
|
66
70
|
|
67
71
|
it 'accepts symbol attributes' do
|
68
|
-
time = Time.
|
72
|
+
time = Time.at(1_451_606_400_000 / 1_000)
|
69
73
|
event = described_class.new(
|
70
74
|
user_id: 123,
|
71
75
|
device_id: 'abcd',
|
@@ -74,6 +78,8 @@ describe AmplitudeAPI::Event do
|
|
74
78
|
user_properties: { 'c' => 'd' },
|
75
79
|
time: time,
|
76
80
|
ip: '127.0.0.1',
|
81
|
+
platform: 'Web',
|
82
|
+
country: 'United States',
|
77
83
|
insert_id: 'bestId'
|
78
84
|
)
|
79
85
|
|
@@ -84,6 +90,8 @@ describe AmplitudeAPI::Event do
|
|
84
90
|
user_properties: { 'c' => 'd' },
|
85
91
|
time: 1_451_606_400_000,
|
86
92
|
ip: '127.0.0.1',
|
93
|
+
platform: 'Web',
|
94
|
+
country: 'United States',
|
87
95
|
insert_id: 'bestId')
|
88
96
|
end
|
89
97
|
end
|
@@ -131,7 +139,7 @@ describe AmplitudeAPI::Event do
|
|
131
139
|
|
132
140
|
describe 'time' do
|
133
141
|
it 'includes a time for the event' do
|
134
|
-
time = Time.
|
142
|
+
time = Time.at(1_451_606_400_000 / 1_000)
|
135
143
|
event = described_class.new(
|
136
144
|
user_id: 123,
|
137
145
|
event_type: 'clicked on home',
|
@@ -168,6 +176,44 @@ describe AmplitudeAPI::Event do
|
|
168
176
|
end
|
169
177
|
end
|
170
178
|
|
179
|
+
describe 'platform' do
|
180
|
+
it 'includes the platform for the event' do
|
181
|
+
event = described_class.new(
|
182
|
+
user_id: 123,
|
183
|
+
event_type: 'clicked on home',
|
184
|
+
platform: 'Web'
|
185
|
+
)
|
186
|
+
expect(event.to_hash[:platform]).to eq('Web')
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'does not include the platform if it is not set' do
|
190
|
+
event = described_class.new(
|
191
|
+
user_id: 123,
|
192
|
+
event_type: 'clicked on home'
|
193
|
+
)
|
194
|
+
expect(event.to_hash).not_to have_key(:platform)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe 'country' do
|
199
|
+
it 'includes the country for the event' do
|
200
|
+
event = described_class.new(
|
201
|
+
user_id: 123,
|
202
|
+
event_type: 'clicked on home',
|
203
|
+
country: 'United States'
|
204
|
+
)
|
205
|
+
expect(event.to_hash[:country]).to eq('United States')
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'does not include the country if it is not set' do
|
209
|
+
event = described_class.new(
|
210
|
+
user_id: 123,
|
211
|
+
event_type: 'clicked on home'
|
212
|
+
)
|
213
|
+
expect(event.to_hash).not_to have_key(:country)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
171
217
|
describe 'revenue params' do
|
172
218
|
it 'includes the price if it is set' do
|
173
219
|
price = 100_000.99
|
@@ -22,6 +22,7 @@ describe AmplitudeAPI do
|
|
22
22
|
described_class.track(event)
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
25
26
|
context 'with only device_id' do
|
26
27
|
it 'sends the event to Amplitude' do
|
27
28
|
event = AmplitudeAPI::Event.new(
|
@@ -38,6 +39,7 @@ describe AmplitudeAPI do
|
|
38
39
|
described_class.track(event)
|
39
40
|
end
|
40
41
|
end
|
42
|
+
|
41
43
|
context 'with both user_id and device_id' do
|
42
44
|
it 'sends the event to Amplitude' do
|
43
45
|
event = AmplitudeAPI::Event.new(
|
@@ -100,6 +102,7 @@ describe AmplitudeAPI do
|
|
100
102
|
described_class.identify(identification)
|
101
103
|
end
|
102
104
|
end
|
105
|
+
|
103
106
|
context 'with only device_id' do
|
104
107
|
it 'sends the identification to Amplitude' do
|
105
108
|
identification = AmplitudeAPI::Identification.new(
|
@@ -119,6 +122,7 @@ describe AmplitudeAPI do
|
|
119
122
|
described_class.identify(identification)
|
120
123
|
end
|
121
124
|
end
|
125
|
+
|
122
126
|
context 'with both user_id and device_id' do
|
123
127
|
it 'sends the identification to Amplitude' do
|
124
128
|
identification = AmplitudeAPI::Identification.new(
|
@@ -169,53 +173,34 @@ describe AmplitudeAPI do
|
|
169
173
|
end
|
170
174
|
end
|
171
175
|
|
172
|
-
describe '.initializer
|
173
|
-
|
174
|
-
|
175
|
-
expect(event.to_hash).to eq(
|
176
|
-
event_type: '',
|
177
|
-
user_id: '',
|
178
|
-
event_properties: {},
|
179
|
-
user_properties: {},
|
180
|
-
ip: ''
|
181
|
-
)
|
182
|
-
end
|
183
|
-
|
184
|
-
it 'initializes event with parameter' do
|
185
|
-
event = AmplitudeAPI::Event.new(
|
176
|
+
describe '.initializer' do
|
177
|
+
let(:attributes) do
|
178
|
+
{
|
186
179
|
user_id: 123,
|
187
180
|
event_type: 'test_event',
|
188
181
|
event_properties: {
|
189
182
|
test_property: 1
|
190
183
|
},
|
191
|
-
ip: '8.8.8.8'
|
192
|
-
)
|
193
|
-
expect(event.to_hash).to eq(
|
194
|
-
event_type: 'test_event',
|
195
|
-
user_id: 123,
|
196
|
-
event_properties: { test_property: 1 },
|
197
184
|
user_properties: {},
|
198
185
|
ip: '8.8.8.8'
|
199
|
-
|
186
|
+
}
|
200
187
|
end
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
)
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
ip: '8.8.8.8'
|
218
|
-
)
|
188
|
+
|
189
|
+
it 'requires event type' do
|
190
|
+
attributes.delete(:event_type)
|
191
|
+
expect { AmplitudeAPI::Event.new(attributes) }.to raise_error(ArgumentError)
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'requires user id or device id' do
|
195
|
+
expect(AmplitudeAPI::Event.new(attributes).to_h).to eq(attributes)
|
196
|
+
attributes.merge!(device_id: 'abc').delete(:user_id)
|
197
|
+
expect(AmplitudeAPI::Event.new(attributes).to_h).to eq(attributes)
|
198
|
+
attributes.delete(:device_id)
|
199
|
+
expect { AmplitudeAPI::Event.new(attributes) }.to raise_error(ArgumentError)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'initializes event with parameter' do
|
203
|
+
expect(AmplitudeAPI::Event.new(attributes)).to eq(attributes)
|
219
204
|
end
|
220
205
|
end
|
221
206
|
|
@@ -276,6 +261,7 @@ describe AmplitudeAPI do
|
|
276
261
|
end
|
277
262
|
end
|
278
263
|
end
|
264
|
+
|
279
265
|
context 'with device_id' do
|
280
266
|
context 'the user is not nil' do
|
281
267
|
it 'sends an event to AmplitudeAPI' do
|
@@ -352,6 +338,7 @@ describe AmplitudeAPI do
|
|
352
338
|
end
|
353
339
|
end
|
354
340
|
end
|
341
|
+
|
355
342
|
context 'with a device_id' do
|
356
343
|
it 'sends an identify to AmplitudeAPI' do
|
357
344
|
identification = AmplitudeAPI::Identification.new(
|
@@ -377,15 +364,89 @@ describe AmplitudeAPI do
|
|
377
364
|
expect(Typhoeus).to receive(:get).with(AmplitudeAPI::SEGMENTATION_URI_STRING,
|
378
365
|
userpwd: "#{described_class.api_key}:#{described_class.secret_key}",
|
379
366
|
params: {
|
380
|
-
e:
|
381
|
-
start:
|
382
|
-
end:
|
383
|
-
s:
|
367
|
+
e: { event_type: 'my event' }.to_json,
|
368
|
+
start: start_time.strftime('%Y%m%d'),
|
369
|
+
end: end_time.strftime('%Y%m%d'),
|
370
|
+
s: [{ prop: 'foo', op: 'is', values: %w[bar] }.to_json]
|
384
371
|
})
|
385
372
|
|
386
373
|
described_class.segmentation({ event_type: 'my event' }, start_time, end_time,
|
387
|
-
s: [{ prop: 'foo', op: 'is', values: %w
|
388
|
-
|
374
|
+
s: [{ prop: 'foo', op: 'is', values: %w[bar] }])
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
describe '.delete' do
|
379
|
+
context 'with user_ids' do
|
380
|
+
it 'sends the deletion to Amplitude' do
|
381
|
+
user_ids = [123, 456, 555]
|
382
|
+
body = {
|
383
|
+
user_ids: user_ids
|
384
|
+
}
|
385
|
+
|
386
|
+
expect(Typhoeus).to receive(:post).with(
|
387
|
+
AmplitudeAPI::DELETION_URI_STRING,
|
388
|
+
userpwd: "#{described_class.api_key}:#{described_class.config.secret_key}",
|
389
|
+
body: body
|
390
|
+
)
|
391
|
+
described_class.delete(user_ids: user_ids)
|
392
|
+
end
|
393
|
+
|
394
|
+
context 'with amplitude_ids' do
|
395
|
+
it 'sends the deletion to Amplitude' do
|
396
|
+
user_ids = [123, 456, 555]
|
397
|
+
amplitude_ids = [122, 456]
|
398
|
+
body = {
|
399
|
+
user_ids: user_ids,
|
400
|
+
amplitude_ids: amplitude_ids
|
401
|
+
}
|
402
|
+
|
403
|
+
expect(Typhoeus).to receive(:post).with(
|
404
|
+
AmplitudeAPI::DELETION_URI_STRING,
|
405
|
+
userpwd: "#{described_class.api_key}:#{described_class.config.secret_key}",
|
406
|
+
body: body
|
407
|
+
)
|
408
|
+
described_class.delete(
|
409
|
+
amplitude_ids: amplitude_ids,
|
410
|
+
user_ids: user_ids
|
411
|
+
)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
context 'with amplitude_ids' do
|
417
|
+
it 'sends the deletion to Amplitude' do
|
418
|
+
amplitude_ids = [122, 456]
|
419
|
+
body = {
|
420
|
+
amplitude_ids: amplitude_ids
|
421
|
+
}
|
422
|
+
|
423
|
+
expect(Typhoeus).to receive(:post).with(
|
424
|
+
AmplitudeAPI::DELETION_URI_STRING,
|
425
|
+
userpwd: "#{described_class.api_key}:#{described_class.config.secret_key}",
|
426
|
+
body: body
|
427
|
+
)
|
428
|
+
described_class.delete(amplitude_ids: amplitude_ids)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
context 'with requester' do
|
433
|
+
it 'sends the deletion to Amplitude' do
|
434
|
+
amplitude_ids = [122, 456]
|
435
|
+
|
436
|
+
body = {
|
437
|
+
amplitude_ids: amplitude_ids,
|
438
|
+
requester: 'privacy@gethopscotch.com'
|
439
|
+
}
|
440
|
+
userpwd = "#{described_class.api_key}:#{described_class.config.secret_key}"
|
441
|
+
|
442
|
+
expect(Typhoeus).to receive(:post).with(AmplitudeAPI::DELETION_URI_STRING,
|
443
|
+
userpwd: userpwd,
|
444
|
+
body: body)
|
445
|
+
described_class.delete(
|
446
|
+
amplitude_ids: amplitude_ids,
|
447
|
+
requester: 'privacy@gethopscotch.com'
|
448
|
+
)
|
449
|
+
end
|
389
450
|
end
|
390
451
|
end
|
391
452
|
|
@@ -416,22 +477,20 @@ describe AmplitudeAPI do
|
|
416
477
|
)
|
417
478
|
body = described_class.track_body(event)
|
418
479
|
|
419
|
-
expected =
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
)
|
434
|
-
expect(body[:event]).to eq(expected)
|
480
|
+
expected = [
|
481
|
+
{
|
482
|
+
event_type: 'test_event',
|
483
|
+
user_id: 23,
|
484
|
+
event_properties: {
|
485
|
+
foo: 'bar'
|
486
|
+
},
|
487
|
+
user_properties: {
|
488
|
+
abc: '123'
|
489
|
+
},
|
490
|
+
ip: '8.8.8.8'
|
491
|
+
}
|
492
|
+
]
|
493
|
+
expect(JSON.parse(body[:event], symbolize_names: true)).to eq(expected)
|
435
494
|
end
|
436
495
|
end
|
437
496
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,83 +1,83 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amplitude-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Rakoczy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: pry
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
- - ">="
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 2.99.0
|
19
|
+
version: 0.12.2
|
23
20
|
type: :development
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
30
|
-
- - ">="
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: 2.99.0
|
26
|
+
version: 0.12.2
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: rake
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
36
30
|
requirements:
|
37
|
-
- - "~>"
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '10.0'
|
40
31
|
- - ">="
|
41
32
|
- !ruby/object:Gem::Version
|
42
|
-
version: '
|
33
|
+
version: '12.0'
|
34
|
+
- - "~>"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '12.0'
|
43
37
|
type: :development
|
44
38
|
prerelease: false
|
45
39
|
version_requirements: !ruby/object:Gem::Requirement
|
46
40
|
requirements:
|
47
|
-
- - "~>"
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '10.0'
|
50
41
|
- - ">="
|
51
42
|
- !ruby/object:Gem::Version
|
52
|
-
version: '
|
43
|
+
version: '12.0'
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '12.0'
|
53
47
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
48
|
+
name: rspec
|
55
49
|
requirement: !ruby/object:Gem::Requirement
|
56
50
|
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.99.0
|
57
54
|
- - "~>"
|
58
55
|
- !ruby/object:Gem::Version
|
59
|
-
version:
|
56
|
+
version: '2.99'
|
60
57
|
type: :development
|
61
58
|
prerelease: false
|
62
59
|
version_requirements: !ruby/object:Gem::Requirement
|
63
60
|
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 2.99.0
|
64
64
|
- - "~>"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version:
|
66
|
+
version: '2.99'
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: typhoeus
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
70
70
|
requirements:
|
71
71
|
- - "~>"
|
72
72
|
- !ruby/object:Gem::Version
|
73
|
-
version: 1.0
|
73
|
+
version: '1.0'
|
74
74
|
type: :runtime
|
75
75
|
prerelease: false
|
76
76
|
version_requirements: !ruby/object:Gem::Requirement
|
77
77
|
requirements:
|
78
78
|
- - "~>"
|
79
79
|
- !ruby/object:Gem::Version
|
80
|
-
version: 1.0
|
80
|
+
version: '1.0'
|
81
81
|
description: Provides an integration for sending events to Amplitude
|
82
82
|
email:
|
83
83
|
- arakoczy@gmail.com
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- ".rubocop.yml"
|
91
91
|
- ".travis.yml"
|
92
92
|
- ".yardopts"
|
93
|
+
- Changelog.md
|
93
94
|
- Gemfile
|
94
95
|
- Gemfile.lock
|
95
96
|
- LICENSE
|
@@ -97,6 +98,7 @@ files:
|
|
97
98
|
- amplitude-api.gemspec
|
98
99
|
- lib/amplitude-api.rb
|
99
100
|
- lib/amplitude_api.rb
|
101
|
+
- lib/amplitude_api/config.rb
|
100
102
|
- lib/amplitude_api/event.rb
|
101
103
|
- lib/amplitude_api/identification.rb
|
102
104
|
- lib/amplitude_api/version.rb
|
@@ -117,15 +119,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
119
|
requirements:
|
118
120
|
- - "~>"
|
119
121
|
- !ruby/object:Gem::Version
|
120
|
-
version: '2.
|
122
|
+
version: '2.2'
|
121
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
124
|
requirements:
|
123
125
|
- - ">="
|
124
126
|
- !ruby/object:Gem::Version
|
125
127
|
version: '0'
|
126
128
|
requirements: []
|
127
|
-
|
128
|
-
rubygems_version: 2.5.2
|
129
|
+
rubygems_version: 3.0.1
|
129
130
|
signing_key:
|
130
131
|
specification_version: 4
|
131
132
|
summary: Send events to the Amplitude API
|