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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0e82b9bf2580f2da3bd76a71cb172ca89d051d3f
4
- data.tar.gz: 8d437b02ece1c511b0f5256ee81f4e4b94fa8494
2
+ SHA256:
3
+ metadata.gz: 8b743d15865f804837f6f3abb9557fa35aa6a32ae360fae0edfeaab0e7905b81
4
+ data.tar.gz: 8c58bd5c4a21a36394cca7ce58da56b1e4135fc4ac574001e31350bda5cf3d69
5
5
  SHA512:
6
- metadata.gz: 0df83a07615c485e264d00522359ab188e93f462e9e30d77ca6053d0dbc771a534062afc2148b60025ca221eddf1378727a8bcac46f25ec2c5f8480a5dd6ab4f
7
- data.tar.gz: e9506a8d285f98d90a80ea28b7e80a337f3749597dbcf3be95f170bb7ca1a66cb5cd810fcbffcb020041ccb620cdb5ee547f6bb382cefd1644f5d10639a25264
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.0.2'
4
+ gem 'typhoeus', '~> 1.1'
5
5
 
6
6
  group :development, :test do
7
- gem 'pry', '~> 0.9.12.6'
8
- gem 'rake', '>= 10.0'
7
+ gem 'pry', '~> 0.12.2'
8
+ gem 'rake', '>= 12.0'
9
9
  gem 'rspec', '>= 2.99.0'
10
- gem 'rubocop', '~> 0.37.2', require: false
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.2.0)
5
- coderay (1.1.1)
6
- diff-lcs (1.2.5)
7
- ethon (0.9.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.9.14)
10
- method_source (0.8.2)
11
- parser (2.3.0.6)
12
- ast (~> 2.2)
13
- powerpack (0.1.1)
14
- pry (0.9.12.6)
15
- coderay (~> 1.0)
16
- method_source (~> 0.8)
17
- slop (~> 3.4)
18
- rainbow (2.1.0)
19
- rake (10.5.0)
20
- rspec (3.4.0)
21
- rspec-core (~> 3.4.0)
22
- rspec-expectations (~> 3.4.0)
23
- rspec-mocks (~> 3.4.0)
24
- rspec-core (3.4.3)
25
- rspec-support (~> 3.4.0)
26
- rspec-expectations (3.4.0)
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.4.0)
29
- rspec-mocks (3.4.1)
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.4.0)
32
- rspec-support (3.4.1)
33
- rubocop (0.37.2)
34
- parser (>= 2.3.0.4, < 3.0)
35
- powerpack (~> 0.1)
36
- rainbow (>= 1.99.1, < 3.0)
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 (~> 0.3)
39
- rubocop-rspec (1.4.0)
40
- ruby-progressbar (1.7.5)
41
- slop (3.6.0)
42
- typhoeus (1.0.2)
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 (0.3.1)
47
+ unicode-display_width (1.5.0)
45
48
 
46
49
  PLATFORMS
47
50
  ruby
48
51
 
49
52
  DEPENDENCIES
50
- pry (~> 0.9.12.6)
51
- rake (>= 10.0)
53
+ pry (~> 0.12.2)
54
+ rake (>= 12.0)
52
55
  rspec (>= 2.99.0)
53
- rubocop (~> 0.37.2)
56
+ rubocop (~> 0.66.0)
54
57
  rubocop-rspec
55
- typhoeus (~> 1.0.2)
58
+ typhoeus (~> 1.1)
56
59
 
57
60
  BUNDLED WITH
58
- 1.10.6
61
+ 1.17.2
@@ -1,5 +1,4 @@
1
- # coding: utf-8
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.add_development_dependency 'rake', '~> 10.0', '>= 10.0'
23
- spec.add_development_dependency 'pry', '~> 0.9.12.6'
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
- # @!attribute [ rw ] api_key
17
- # @return [ String ] an Amplitude API Key
18
- attr_accessor :api_key
18
+ def config
19
+ Config.instance
20
+ end
21
+
22
+ def configure
23
+ yield config
24
+ end
19
25
 
20
- # @!attribute [ rw ] secret_key
21
- # @return [ String ] an Amplitude Secret Key
22
- attr_accessor :secret_key
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(e, start_time, end_time, **options)
154
+ def segmentation(event, start_time, end_time, **options)
145
155
  Typhoeus.get SEGMENTATION_URI_STRING, userpwd: "#{api_key}:#{secret_key}", params: {
146
- e: e.to_json,
147
- m: options[:m],
156
+ e: event.to_json,
157
+ m: options[:m],
148
158
  start: start_time.strftime('%Y%m%d'),
149
- end: end_time.strftime('%Y%m%d'),
150
- i: options[:i],
151
- s: (options[:s] || []).map(&:to_json),
152
- g: options[: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
@@ -1,71 +1,19 @@
1
1
  class AmplitudeAPI
2
2
  # AmplitudeAPI::Event
3
3
  class Event
4
- # @!attribute [ rw ] user_id
5
- # @return [ String ] the user_id to be sent to Amplitude
6
- attr_accessor :user_id
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
- # @param [ String ] user_id a user_id to associate with the event
49
- # @param [ String ] device_id a device_id to associate with the event
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
- self.user_id = getopt(attributes, :user_id, '')
61
- self.device_id = getopt(attributes, :device_id, nil)
62
- self.event_type = getopt(attributes, :event_type, '')
63
- self.event_properties = getopt(attributes, :event_properties, {})
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
- serialized_event = {}
85
- serialized_event[:event_type] = event_type
86
- serialized_event[:user_id] = user_id
87
- serialized_event[:event_properties] = event_properties
88
- serialized_event[:user_properties] = user_properties
89
- serialized_event = add_optional_properties(serialized_event)
90
- serialized_event.merge(revenue_hash)
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 [ Hash ] A serialized Event with optional properties
94
- def add_optional_properties(serialized_event)
95
- serialized_event[:device_id] = device_id if device_id
96
- serialized_event[:time] = formatted_time if time
97
- serialized_event[:ip] = ip if ip
98
- serialized_event[:insert_id] = insert_id if insert_id
99
- serialized_event
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
- if other.respond_to?(:to_hash)
107
- to_hash == other.to_hash
108
- else
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.to_i * 1_000
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(options)
120
- self.price = getopt(options, :price)
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
- attr_accessor :user_id
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
@@ -1,3 +1,3 @@
1
1
  class AmplitudeAPI
2
- VERSION = '0.0.10'.freeze
2
+ VERSION = '0.1.0'.freeze
3
3
  end
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.parse('2016-01-01 00:00:00 -0000')
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.parse('2016-01-01 00:00:00 -0000')
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.parse('2016-01-01 00:00:00 -0000')
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 ' do
173
- it 'initializes event without parameter' do
174
- event = AmplitudeAPI::Event.new
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
- it 'initializes event with parameter including device_id' do
202
- event = AmplitudeAPI::Event.new(
203
- user_id: 123,
204
- device_id: 'abc',
205
- event_type: 'test_event',
206
- event_properties: {
207
- test_property: 1
208
- },
209
- ip: '8.8.8.8'
210
- )
211
- expect(event.to_hash).to eq(
212
- event_type: 'test_event',
213
- user_id: 123,
214
- device_id: 'abc',
215
- event_properties: { test_property: 1 },
216
- user_properties: {},
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: { event_type: 'my event' }.to_json,
381
- start: start_time.strftime('%Y%m%d'),
382
- end: end_time.strftime('%Y%m%d'),
383
- s: [{ prop: 'foo', op: 'is', values: %w(bar) }.to_json]
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(bar) }]
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 = JSON.generate(
420
- [
421
- {
422
- event_type: 'test_event',
423
- user_id: 23,
424
- event_properties: {
425
- foo: 'bar'
426
- },
427
- user_properties: {
428
- abc: '123'
429
- },
430
- ip: '8.8.8.8'
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
@@ -17,6 +17,7 @@ RSpec.configure do |config|
17
17
  config.order = 'random'
18
18
 
19
19
  config.before(:suite) do
20
- AmplitudeAPI.api_key = 'stub api key'
20
+ AmplitudeAPI.config.api_key = 'stub api key'
21
+ AmplitudeAPI.config.secret_key = 'stub secret key'
21
22
  end
22
23
  end
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.10
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: 2017-09-14 00:00:00.000000000 Z
11
+ date: 2019-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rspec
14
+ name: pry
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.99'
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: '2.99'
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: '10.0'
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: '10.0'
43
+ version: '12.0'
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '12.0'
53
47
  - !ruby/object:Gem::Dependency
54
- name: pry
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: 0.9.12.6
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: 0.9.12.6
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.2
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.2
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.0'
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
- rubyforge_project:
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