amplitude-api 0.1.0 → 0.3.3

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
2
  SHA256:
3
- metadata.gz: 8b743d15865f804837f6f3abb9557fa35aa6a32ae360fae0edfeaab0e7905b81
4
- data.tar.gz: 8c58bd5c4a21a36394cca7ce58da56b1e4135fc4ac574001e31350bda5cf3d69
3
+ metadata.gz: e4372cbca5a551be9721be89fbbe3ee80dc9216d8fa44fba7b0031a5e62785cd
4
+ data.tar.gz: ee6056b7e64c2b51f9365a8e57c90a1b4b8fc5edc406b1f7cbabf4caf831b7ad
5
5
  SHA512:
6
- metadata.gz: 3bf4707b9fe9905e95fdeee4c66044dfce02a2f4bae7fe939cffc343957ff4b2eb3d0dcf41d9c9dc958048f14fc8039c5d4e5c375103b7aeec4260e5a5a5c2d5
7
- data.tar.gz: 6cb4d59c569e8ccb3d8fc1f52920baba4ae6fa1da605115e6ed393a05119d1b275e58d00d18349cb840dd84b11b58416f3639e31eb95e4fb6c88eac55a378084
6
+ metadata.gz: f55a3e558f985bac1d78ac842532dfd5dfc69290f7ece8f9c197c71f155ae802de8a00649c5be9bcdd4a67e995e59eaf9cbcc3586808ddfedbb00aba259cf39b
7
+ data.tar.gz: 3e6d679080a879ccf02ab29b3f13839b29c78bfb28cd48793275345ca41b77a61d09231e82d3cc4132319d14eef0ac807e5fa589330a1e0f84486fa534f875a4
data/.gitignore CHANGED
@@ -27,11 +27,12 @@ build/
27
27
 
28
28
  # for a library or gem, you might want to ignore these files since the code is
29
29
  # intended to run in multiple environments; otherwise, check them in:
30
- # Gemfile.lock
31
- # .ruby-version
32
- # .ruby-gemset
30
+ Gemfile.lock
31
+ .ruby-version
32
+ .ruby-gemset
33
33
 
34
34
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35
35
  .rvmrc
36
36
 
37
- .idea
37
+ .idea
38
+ amplitude-api.iml
data/.rubocop.yml CHANGED
@@ -1,8 +1,26 @@
1
1
  require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.4
5
+
2
6
  Metrics/LineLength:
3
7
  Max: 120
4
8
 
9
+ Style/StringLiterals:
10
+ EnforcedStyle: double_quotes
11
+ SupportedStyles:
12
+ - single_quotes
13
+ - double_quotes
14
+
15
+ Style/BlockDelimiters:
16
+ Enabled: true
17
+ Exclude:
18
+ - spec/**/*
19
+
5
20
  # Disable some failing rspec cops that fail from upgrading gems until we can clean things up a bit.
21
+ Naming/FileName:
22
+ Exclude:
23
+ - lib/amplitude-api.rb
6
24
  RSpec/ExampleLength:
7
25
  Enabled: false
8
26
  Metrics/BlockLength:
@@ -14,4 +32,4 @@ RSpec/NestedGroups:
14
32
  RSpec/MessageSpies:
15
33
  Enabled: false
16
34
  RSpec/ContextWording:
17
- Enabled: false
35
+ Enabled: false
data/.travis.yml CHANGED
@@ -1,6 +1,5 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  rvm:
4
- - 2.3.1
5
- - 2.2.2
6
- - 2.1.6
4
+ - 2.6
5
+ - 2.4
data/Changelog.md CHANGED
@@ -3,6 +3,38 @@
3
3
  We would like to think our many [contributors](https://github.com/toothrot/amplitude-api/graphs/contributors) for
4
4
  suggestions, ideas and improvements to Amplitude API.
5
5
 
6
+ ## 0.3.3 (2021-02-24)
7
+ * Relaxes Faraday dependency
8
+
9
+ ## 0.3.2 (2021-02-23)
10
+ * Creates new properties on initialization
11
+
12
+ ## 0.3.1 (2021-02-23)
13
+ * Allows sending options to Amplitude
14
+ * Solves an error when accessing event properties not been created yet
15
+
16
+ ## 0.3.0 (2021-02-22)
17
+
18
+ * Changes Typhoeus to Faraday to launch requests (**breaking change**)
19
+ * Adds new API fields to Event
20
+ * Event can now include arbitrary properties, so it could be used if the API adds new ones.
21
+
22
+ ## 0.2.0 (2021-02-14)
23
+
24
+ * Updates gem to use HTTP API V2.
25
+
26
+ ## 0.1.1 (2019-01-01)
27
+
28
+ * Fix #41 - Delete API now correctly handles Arrays of IDs.
29
+
30
+ ## 0.1.0 (2019-01-01)
31
+
32
+ * Update Gem dependencies (thanks @kolorahl, @itamar, @krettan)
33
+ * Minimum ruby version is now 2.2
34
+ * Support Delete API (thanks @samjohn)
35
+ * Fix bundle Inline (thanks @jonduarte)
36
+ * Many fixes from @kolorahl
37
+
6
38
  ## 0.0.10 (2017-09-13)
7
39
 
8
40
  * Allow to use "Event Segmentation" via API ([#23](https://github.com/toothrot/amplitude-api/pull/23)).
data/Gemfile CHANGED
@@ -1,12 +1,3 @@
1
- # Generated from /Users/alex/development/amplitude-api/amplitude-api.gemspec
2
- source 'https://rubygems.org'
3
-
4
- gem 'typhoeus', '~> 1.1'
5
-
6
- group :development, :test do
7
- gem 'pry', '~> 0.12.2'
8
- gem 'rake', '>= 12.0'
9
- gem 'rspec', '>= 2.99.0'
10
- gem 'rubocop', '~> 0.66.0', require: false
11
- gem 'rubocop-rspec'
12
- end
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+ gemspec
data/Rakefile CHANGED
@@ -1,14 +1,16 @@
1
- require 'bundler/gem_tasks'
2
- require 'rubocop/rake_task'
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
3
5
 
4
6
  RuboCop::RakeTask.new
5
7
 
6
8
  begin
7
- require 'rspec/core/rake_task'
9
+ require "rspec/core/rake_task"
8
10
  RSpec::Core::RakeTask.new(:spec)
9
11
  rescue LoadError
10
- puts 'Unable to load rspec. Have you run `bundle install`?'
12
+ puts "Unable to load rspec. Have you run `bundle install`?"
11
13
  end
12
14
 
13
15
  task(:default).clear
14
- task default: ['rubocop:auto_correct', :spec]
16
+ task default: ["rubocop:auto_correct", :spec]
@@ -1,25 +1,27 @@
1
- lib = File.expand_path('lib', __dir__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'amplitude_api/version'
5
+ require "amplitude_api/version"
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = 'amplitude-api'
8
+ spec.name = "amplitude-api"
7
9
  spec.version = AmplitudeAPI::VERSION
8
- spec.authors = ['Alex Rakoczy']
9
- spec.email = ['arakoczy@gmail.com']
10
- spec.summary = 'Send events to the Amplitude API'
11
- spec.description = 'Provides an integration for sending events to Amplitude'
12
- spec.homepage = 'https://github.com/toothrot/amplitude-api'
13
- spec.license = 'MIT'
10
+ spec.authors = ["Alex Rakoczy"]
11
+ spec.email = ["arakoczy@gmail.com"]
12
+ spec.summary = "Send events to the Amplitude API"
13
+ spec.description = "Provides an integration for sending events to Amplitude"
14
+ spec.homepage = "https://github.com/toothrot/amplitude-api"
15
+ spec.license = "MIT"
14
16
 
15
17
  spec.files = `git ls-files -z`.split("\x0")
16
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ['lib']
20
+ spec.require_paths = ["lib"]
19
21
 
20
- spec.add_development_dependency 'pry', '~> 0.12.2'
21
- spec.add_development_dependency 'rake', '~> 12.0', '>= 12.0'
22
- spec.add_development_dependency 'rspec', '~> 2.99', '>= 2.99.0'
23
- spec.add_dependency 'typhoeus', '~> 1.0'
24
- spec.required_ruby_version = '~> 2.2'
22
+ spec.add_development_dependency "pry", "~> 0.12.2"
23
+ spec.add_development_dependency "rake", "~> 12.0", ">= 12.0"
24
+ spec.add_development_dependency "rspec", "~> 2.99", ">= 2.99.0"
25
+ spec.add_dependency "faraday", "~> 1.0"
26
+ spec.required_ruby_version = ">= 2.4"
25
27
  end
data/lib/amplitude-api.rb CHANGED
@@ -1,4 +1,3 @@
1
- # rubocop:disable Style/FileName
2
- require_relative 'amplitude_api'
3
- # rubocop:enable Style/FileName
4
- # Whoops.
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "amplitude_api"
data/lib/amplitude_api.rb CHANGED
@@ -1,18 +1,20 @@
1
- require 'json'
2
- require 'typhoeus'
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "faraday"
3
5
 
4
6
  # AmplitudeAPI
5
7
  class AmplitudeAPI
6
- require_relative 'amplitude_api/config'
7
- require_relative 'amplitude_api/event'
8
- require_relative 'amplitude_api/identification'
8
+ require_relative "amplitude_api/config"
9
+ require_relative "amplitude_api/event"
10
+ require_relative "amplitude_api/identification"
9
11
 
10
- TRACK_URI_STRING = 'https://api.amplitude.com/httpapi'.freeze
11
- IDENTIFY_URI_STRING = 'https://api.amplitude.com/identify'.freeze
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
+ TRACK_URI_STRING = "https://api.amplitude.com/2/httpapi"
13
+ IDENTIFY_URI_STRING = "https://api.amplitude.com/identify"
14
+ SEGMENTATION_URI_STRING = "https://amplitude.com/api/2/events/segmentation"
15
+ DELETION_URI_STRING = "https://amplitude.com/api/2/deletions/users"
14
16
 
15
- USER_WITH_NO_ACCOUNT = "user who doesn't have an account".freeze
17
+ USER_WITH_NO_ACCOUNT = "user who doesn't have an account"
16
18
 
17
19
  class << self
18
20
  def config
@@ -38,12 +40,12 @@ class AmplitudeAPI
38
40
  # @param [ String ] event_name a string that describes the event, e.g. "clicked on Home"
39
41
  # @param [ String ] user a string or integer that uniquely identifies a user.
40
42
  # @param [ String ] device a string that uniquely identifies the device.
41
- # @param [ Hash ] event_properties a hash that is serialized to JSON,
43
+ # @option options [ Hash ] event_properties a hash that is serialized to JSON,
42
44
  # and can contain any other property to be stored on the Event
43
- # @param [ Hash ] user_properties a hash that is serialized to JSON,
45
+ # @option options [ Hash ] user_properties a hash that is serialized to JSON,
44
46
  # and contains user properties to be associated with the user
45
47
  #
46
- # @return [ Typhoeus::Response ]
48
+ # @return [ Faraday::Response ]
47
49
  def send_event(event_name, user, device, options = {})
48
50
  event = AmplitudeAPI::Event.new(
49
51
  user_id: user,
@@ -68,10 +70,13 @@ class AmplitudeAPI
68
70
  def track_body(*events)
69
71
  event_body = events.flatten.map(&:to_hash)
70
72
 
71
- {
73
+ body = {
72
74
  api_key: api_key,
73
- event: JSON.generate(event_body)
75
+ events: event_body
74
76
  }
77
+ body[:options] = config.options if config.options
78
+
79
+ JSON.generate(body)
75
80
  end
76
81
 
77
82
  # @overload track(event)
@@ -80,11 +85,11 @@ class AmplitudeAPI
80
85
  # @overload track([events])
81
86
  # @param [ Array<AmplitudeAPI::Event> ] Send an array of events in a single request to Amplitude
82
87
  #
83
- # @return [ Typhoeus::Response ]
88
+ # @return [ Faraday::Response ]
84
89
  #
85
90
  # Send one or more Events to the Amplitude API
86
91
  def track(*events)
87
- Typhoeus.post(TRACK_URI_STRING, body: track_body(events))
92
+ Faraday.post(TRACK_URI_STRING, track_body(events), "Content-Type" => "application/json")
88
93
  end
89
94
 
90
95
  # ==== Identification related methods
@@ -123,40 +128,40 @@ class AmplitudeAPI
123
128
  # @overload identify([identifications])
124
129
  # @param [ Array<AmplitudeAPI::Identify> ] Send an array of identifications in a single request to Amplitude
125
130
  #
126
- # @return [ Typhoeus::Response ]
131
+ # @return [ Faraday::Response ]
127
132
  #
128
133
  # Send one or more Identifications to the Amplitude Identify API
129
134
  def identify(*identifications)
130
- Typhoeus.post(IDENTIFY_URI_STRING, body: identify_body(identifications))
135
+ Faraday.post(IDENTIFY_URI_STRING, identify_body(identifications))
131
136
  end
132
137
 
133
138
  # ==== Event Segmentation related methods
134
139
 
135
140
  # Get metrics for an event with segmentation.
136
141
  #
137
- # @param [ Hash ] e a hash that defines event.
142
+ # @param [ Hash ] event a hash that defines event.
138
143
  # @param [ Time ] start_time a start time.
139
144
  # @param [ Time ] end_time a end time.
140
- # @param [ String ] m a string that defines aggregate function.
145
+ # @option options [ String ] m a string that defines aggregate function.
141
146
  # For non-property metrics: "uniques", "totals", "pct_dau", or "average" (default: "uniques").
142
147
  # For property metrics: "histogram", "sums", or "value_avg"
143
148
  # (note: a valid "group_by" value is required in parameter e).
144
- # @param [ Integer ] i an integer that defines segmentation interval.
149
+ # @option options [ Integer ] i an integer that defines segmentation interval.
145
150
  # Set to -300000, -3600000, 1, 7, or 30 for realtime, hourly, daily, weekly,
146
151
  # and monthly counts, respectively (default: 1). Realtime segmentation is capped at 2 days,
147
152
  # hourly segmentation is capped at 7 days, and daily at 365 days.
148
- # @param [ Array ] s an array that defines segment definitions.
149
- # @param [ String ] g a string that defines property to group by.
150
- # @param [ Integer ] limit an integer that defines number of Group By values
153
+ # @option options [ Array ] s an array that defines segment definitions.
154
+ # @option options [ String ] g a string that defines property to group by.
155
+ # @option options [ Integer ] limit an integer that defines number of Group By values
151
156
  # returned (default: 100). The maximum limit is 1000.
152
157
  #
153
- # @return [ Typhoeus::Response ]
158
+ # @return [ Faraday::Response ]
154
159
  def segmentation(event, start_time, end_time, **options)
155
- Typhoeus.get SEGMENTATION_URI_STRING, userpwd: "#{api_key}:#{secret_key}", params: {
160
+ Faraday.get SEGMENTATION_URI_STRING, userpwd: "#{api_key}:#{secret_key}", params: {
156
161
  e: event.to_json,
157
162
  m: options[:m],
158
- start: start_time.strftime('%Y%m%d'),
159
- end: end_time.strftime('%Y%m%d'),
163
+ start: start_time.strftime("%Y%m%d"),
164
+ end: end_time.strftime("%Y%m%d"),
160
165
  i: options[:i],
161
166
  s: (options[:s] || []).map(&:to_json),
162
167
  g: options[:g],
@@ -164,27 +169,48 @@ class AmplitudeAPI
164
169
  }.delete_if { |_, value| value.nil? }
165
170
  end
166
171
 
167
- # ==== GDPR compliance methods
168
-
169
- # Delete a user from amplitude when they request it to comply with GDPR
172
+ # Delete a user from amplitude
173
+ #
170
174
  # You must pass in either an array of user_ids or an array of amplitude_ids
171
175
  #
172
- # @param [ user_ids ] (optional) the user_ids to delete
176
+ # @param [ Array<String> ] (optional) the user_ids to delete
173
177
  # based on your database
174
- # @param [ amplitude_ids ] (optional) the amplitude_ids to delete
178
+ # @param [ Array<Integer> ] (optional) the amplitude_ids to delete
175
179
  # based on the amplitude database
176
- # @param [ requester ] the email address of the person who
180
+ # @param [ String ] requester the email address of the person who
177
181
  # 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? }
182
+ # @param [ Boolean ] (optional) ignore any invalid user IDs(users that do no
183
+ # exist in the project) that were passed in
184
+ # @param [ Boolean ] (optional) delete from the entire org rather than just
185
+ # this project.
186
+ # @return [ Faraday::Response ]
187
+ def delete(user_ids: nil, amplitude_ids: nil, requester: nil, ignore_invalid_id: nil, delete_from_org: nil)
188
+ user_ids = Array(user_ids)
189
+ amplitude_ids = Array(amplitude_ids)
190
+
191
+ faraday = Faraday.new do |conn|
192
+ conn.basic_auth config.api_key, config.secret_key
193
+ end
194
+
195
+ faraday.post(
196
+ DELETION_URI_STRING,
197
+ delete_body(user_ids, amplitude_ids, requester, ignore_invalid_id, delete_from_org),
198
+ "Content-Type" => "application/json"
199
+ )
200
+ end
201
+
202
+ private
203
+
204
+ def delete_body(user_ids, amplitude_ids, requester, ignore_invalid_id, delete_from_org)
205
+ body = {
206
+ amplitude_ids: amplitude_ids,
207
+ user_ids: user_ids,
208
+ requester: requester
209
+ }.delete_if { |_, value| value.nil? || value.empty? }
210
+
211
+ body[:ignore_invalid_id] = ignore_invalid_id.to_s if ignore_invalid_id
212
+ body[:delete_from_org] = delete_from_org.to_s if delete_from_org
213
+ JSON.generate(body)
188
214
  end
189
215
  end
190
216
  end
@@ -1,4 +1,6 @@
1
- require 'singleton'
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
2
4
 
3
5
  class AmplitudeAPI
4
6
  # AmplitudeAPI::Config
@@ -6,20 +8,40 @@ class AmplitudeAPI
6
8
  include Singleton
7
9
 
8
10
  attr_accessor :api_key, :secret_key, :whitelist, :time_formatter,
9
- :event_properties_formatter, :user_properties_formatter
11
+ :event_properties_formatter, :user_properties_formatter,
12
+ :options
10
13
 
11
14
  def initialize
12
15
  self.class.defaults.each { |k, v| send("#{k}=", v) }
13
16
  end
14
17
 
15
18
  class << self
19
+ def base_properties
20
+ %i[event_type event_properties user_properties user_id device_id]
21
+ end
22
+
23
+ def revenue_properties
24
+ %i[revenue_type product_id revenue price quantity]
25
+ end
26
+
27
+ def optional_properties
28
+ %i[
29
+ time
30
+ ip platform country insert_id
31
+ groups app_version os_name os_version
32
+ device_brand device_manufacturer device_model
33
+ carrier region city dma language
34
+ location_lat location_lng
35
+ idfa idfv adid android_id
36
+ event_id session_id
37
+ ]
38
+ end
39
+
16
40
  def defaults
17
41
  {
18
42
  api_key: nil,
19
43
  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],
44
+ whitelist: base_properties + revenue_properties + optional_properties,
23
45
  time_formatter: ->(time) { time ? time.to_i * 1_000 : nil },
24
46
  event_properties_formatter: ->(props) { props || {} },
25
47
  user_properties_formatter: ->(props) { props || {} }